J3W1 / DESIGN SYSTEM
i3 window frame
The workstation frame; an i3 bar with workspaces and Chinese i3status labels above the terminal and code-editor components as tiled, tabbed or floating windows behind i3 title bars.
stable · LIGHT DOM · 1.1.0
Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.
Live variants
default
~/theme main ls -l ports total 5 drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 alacritty drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 gtk -rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 README.md -rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 verify.sh lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 xresources -> ../tokens/ -rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 evidence.tar.gz ~/theme main cat ports/gtk/port.json cat: ports/gtk/port.json: No such file or directory ~/theme main npm run validate
// Compose the window title of the focused container. import { Container } from "./tree.js"; const MAX_LENGTH = 48; export function title(container) { const name = container.name ?? "untitled…"; let label = `${name} (${container.children.length})`; if (label.length > MAX_LENGTH) { label = label.slice(0, MAX_LENGTH) + "…"; } return escape(label); /* escape() is deprecated */ } const broken = 0x1G;
Workspace 2 is empty.
Workspace 3 is empty.
<j3w1-i3-window-frame id="example-i3-window-frame-default"><div class="i3-window-frame" role="region" aria-label="Workstation, workspace 1: theme, tiled">
<div class="i3-window-frame-bar" role="group" aria-label="i3 bar">
<div class="i3-window-frame-workspaces" role="tablist" aria-label="Workspaces">
<button class="i3-window-frame-workspace" type="button" role="tab" aria-selected="true" tabindex="0" id="example-i3-window-frame-default-workspace-tab-0" aria-controls="example-i3-window-frame-default-workspace-panel-0">1:theme</button>
<button class="i3-window-frame-workspace i3-window-frame-workspace-demo-target" type="button" role="tab" aria-selected="false" tabindex="-1" id="example-i3-window-frame-default-workspace-tab-1" aria-controls="example-i3-window-frame-default-workspace-panel-1">2:reference</button>
<button class="i3-window-frame-workspace i3-window-frame-workspace-urgent" type="button" role="tab" aria-selected="false" tabindex="-1" id="example-i3-window-frame-default-workspace-tab-2" aria-controls="example-i3-window-frame-default-workspace-panel-2">3:ports<span class="i3-window-frame-urgent-mark">!<span class="sr-only"> urgent</span></span></button>
</div>
<div class="i3-window-frame-status" role="group" lang="zh" aria-label="状态">
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 处理器 12%</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 内存储器 3.1G</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 局域网 up</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 电池 87%</span>
<time class="i3-window-frame-block i3-window-frame-clock" datetime="2026-09-06T14:32:07">09月06号 14时32分07秒</time>
</div>
</div>
<section role="tabpanel" id="example-i3-window-frame-default-workspace-panel-0" aria-labelledby="example-i3-window-frame-default-workspace-tab-0"><div class="i3-window-frame-area">
<div class="i3-window-frame-window i3-window-frame-focused" role="group" aria-label="Terminal window, focused" tabindex="0">
<div class="i3-window-frame-title">~/theme — zsh</div>
<pre class="terminal terminal-transcript" role="log" aria-label="Shell transcript" tabindex="0"><span class="terminal-line"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> ls -l ports</span>
<span class="terminal-line">total 5</span>
<span class="terminal-line">drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 <span class="terminal-fg-5 terminal-bold">alacritty</span></span>
<span class="terminal-line">drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 <span class="terminal-fg-5 terminal-bold">gtk</span></span>
<span class="terminal-line">-rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 <span class="terminal-sel">README.md</span></span>
<span class="terminal-line"><span class="terminal-sel">-rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 </span><span class="terminal-sel terminal-fg-3">verify.sh</span></span>
<span class="terminal-line">lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 <span class="terminal-fg-7">xresources</span> -> <span class="terminal-fg-2">../tokens/</span></span>
<span class="terminal-line">-rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 <span class="terminal-fg-11">evidence.tar.gz</span></span>
<span class="terminal-line"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> cat ports/gtk/port.json</span>
<span class="terminal-line terminal-fg-5">cat: ports/gtk/port.json: No such file or directory</span>
<span class="terminal-line terminal-line-cursor"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> npm run validate<span class="terminal-cursor" aria-hidden="true"> </span></span></pre>
</div>
<div class="i3-window-frame-window" role="group" aria-label="Editor window" tabindex="0">
<div class="i3-window-frame-title">title.js — editor</div>
<div class="code-editor" role="textbox" aria-multiline="true" aria-readonly="true" aria-label="title.js, read-only" tabindex="0">
<pre class="code-editor-pre"><span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">1</span><span class="code-editor-text"><span class="code-editor-cm">// Compose the window title of the focused container.</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">2</span><span class="code-editor-text"><span class="code-editor-kw">import</span> <span class="code-editor-punct">{</span> <span class="code-editor-type">Container</span> <span class="code-editor-punct">}</span> <span class="code-editor-kw">from</span> <span class="code-editor-str">"./tree.js"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">3</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-const">MAX_LENGTH</span> <span class="code-editor-op">=</span> <span class="code-editor-num">48</span><span class="code-editor-punct">;</span><span class="code-editor-ws" aria-hidden="true">··</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">4</span><span class="code-editor-text"><span class="code-editor-kw">export</span> <span class="code-editor-kw">function</span> <span class="code-editor-fn">title</span><span class="code-editor-punct code-editor-bracket">(</span><span class="code-editor-var">container</span><span class="code-editor-punct code-editor-bracket">)</span> <span class="code-editor-punct">{</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">5</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">const</span> <span class="code-editor-var">name</span> <span class="code-editor-op">=</span> <span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">name</span> <span class="code-editor-op">??</span> <span class="code-editor-str">"untitled<span class="code-editor-esc">…</span>"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line code-editor-line-current"><span class="code-editor-ln" aria-hidden="true">6</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">let</span> <span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-str">`${</span><span class="code-editor-var">name</span><span class="code-editor-str">} (${</span><span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">children</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span><span class="code-editor-str">})`</span><span class="code-editor-punct">;</span><span class="code-editor-caret" aria-hidden="true"></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">7</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">if</span> <span class="code-editor-punct">(</span><span class="code-editor-sel"><span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span> <span class="code-editor-op">></span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span> <span class="code-editor-punct">{</span></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">8</span><span class="code-editor-text"><span class="code-editor-sel"><span class="code-editor-indent"> </span><span class="code-editor-indent"> </span><span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-fn">slice</span><span class="code-editor-punct">(</span><span class="code-editor-num">0</span><span class="code-editor-punct">,</span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span></span> <span class="code-editor-op">+</span> <span class="code-editor-str">"…"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">9</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">10</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">return</span> <span class="code-editor-fn code-editor-deprecated">escape</span><span class="code-editor-punct">(</span><span class="code-editor-var">label</span><span class="code-editor-punct">);</span> <span class="code-editor-cm">/* escape() is deprecated */</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">11</span><span class="code-editor-text"><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">12</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-var">broken</span> <span class="code-editor-op">=</span> <span class="code-editor-num">0x1</span><span class="code-editor-invalid">G</span><span class="code-editor-punct">;</span></span></span></pre>
</div>
</div>
</div></section>
<section role="tabpanel" id="example-i3-window-frame-default-workspace-panel-1" aria-labelledby="example-i3-window-frame-default-workspace-tab-1" hidden=""><p>Workspace 2 is empty.</p></section><section role="tabpanel" id="example-i3-window-frame-default-workspace-panel-2" aria-labelledby="example-i3-window-frame-default-workspace-tab-2" hidden=""><p>Workspace 3 is empty.</p></section></div></j3w1-i3-window-frame>tabbed
~/theme main ls -l ports total 5 drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 alacritty drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 gtk -rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 README.md -rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 verify.sh lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 xresources -> ../tokens/ -rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 evidence.tar.gz ~/theme main cat ports/gtk/port.json cat: ports/gtk/port.json: No such file or directory ~/theme main npm run validate
// Compose the window title of the focused container. import { Container } from "./tree.js"; const MAX_LENGTH = 48; export function title(container) { const name = container.name ?? "untitled…"; let label = `${name} (${container.children.length})`; if (label.length > MAX_LENGTH) { label = label.slice(0, MAX_LENGTH) + "…"; } return escape(label); /* escape() is deprecated */ } const broken = 0x1G;
Workspace 2 is empty.
Workspace 3 is empty.
<j3w1-i3-window-frame id="example-i3-window-frame-tabbed"><div class="i3-window-frame" role="region" aria-label="Workstation, workspace 1: theme, tabbed">
<div class="i3-window-frame-bar" role="group" aria-label="i3 bar">
<div class="i3-window-frame-workspaces" role="tablist" aria-label="Workspaces">
<button class="i3-window-frame-workspace" type="button" role="tab" aria-selected="true" tabindex="0" id="example-i3-window-frame-tabbed-workspace-tab-0" aria-controls="example-i3-window-frame-tabbed-workspace-panel-0">1:theme</button>
<button class="i3-window-frame-workspace i3-window-frame-workspace-demo-target" type="button" role="tab" aria-selected="false" tabindex="-1" id="example-i3-window-frame-tabbed-workspace-tab-1" aria-controls="example-i3-window-frame-tabbed-workspace-panel-1">2:reference</button>
<button class="i3-window-frame-workspace i3-window-frame-workspace-urgent" type="button" role="tab" aria-selected="false" tabindex="-1" id="example-i3-window-frame-tabbed-workspace-tab-2" aria-controls="example-i3-window-frame-tabbed-workspace-panel-2">3:ports<span class="i3-window-frame-urgent-mark">!<span class="sr-only"> urgent</span></span></button>
</div>
<div class="i3-window-frame-status" role="group" lang="zh" aria-label="状态">
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 处理器 12%</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 内存储器 3.1G</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 局域网 up</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 电池 87%</span>
<time class="i3-window-frame-block i3-window-frame-clock" datetime="2026-09-06T14:32:07">09月06号 14时32分07秒</time>
</div>
</div>
<section role="tabpanel" id="example-i3-window-frame-tabbed-workspace-panel-0" aria-labelledby="example-i3-window-frame-tabbed-workspace-tab-0"><div class="i3-window-frame-area">
<div class="i3-window-frame-container i3-window-frame-focused" role="group" aria-label="Tabbed container, focused" tabindex="0">
<div class="i3-window-frame-tabs" role="tablist" aria-label="Windows in the container">
<button class="i3-window-frame-tab" type="button" role="tab" id="example-i3-window-frame-tabbed-i3-tab-terminal" aria-selected="true" aria-controls="example-i3-window-frame-tabbed-i3-panel-terminal" tabindex="0">~/theme — zsh</button>
<button class="i3-window-frame-tab" type="button" role="tab" id="example-i3-window-frame-tabbed-i3-tab-editor" aria-selected="false" aria-controls="example-i3-window-frame-tabbed-i3-panel-editor" tabindex="-1">title.js — editor</button>
</div>
<div class="i3-window-frame-panel" role="tabpanel" id="example-i3-window-frame-tabbed-i3-panel-terminal" aria-labelledby="example-i3-window-frame-tabbed-i3-tab-terminal">
<pre class="terminal terminal-transcript" role="log" aria-label="Shell transcript" tabindex="0"><span class="terminal-line"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> ls -l ports</span>
<span class="terminal-line">total 5</span>
<span class="terminal-line">drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 <span class="terminal-fg-5 terminal-bold">alacritty</span></span>
<span class="terminal-line">drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 <span class="terminal-fg-5 terminal-bold">gtk</span></span>
<span class="terminal-line">-rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 <span class="terminal-sel">README.md</span></span>
<span class="terminal-line"><span class="terminal-sel">-rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 </span><span class="terminal-sel terminal-fg-3">verify.sh</span></span>
<span class="terminal-line">lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 <span class="terminal-fg-7">xresources</span> -> <span class="terminal-fg-2">../tokens/</span></span>
<span class="terminal-line">-rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 <span class="terminal-fg-11">evidence.tar.gz</span></span>
<span class="terminal-line"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> cat ports/gtk/port.json</span>
<span class="terminal-line terminal-fg-5">cat: ports/gtk/port.json: No such file or directory</span>
<span class="terminal-line terminal-line-cursor"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> npm run validate<span class="terminal-cursor" aria-hidden="true"> </span></span></pre>
</div>
<div class="i3-window-frame-panel" role="tabpanel" id="example-i3-window-frame-tabbed-i3-panel-editor" aria-labelledby="example-i3-window-frame-tabbed-i3-tab-editor" hidden="">
<div class="code-editor" role="textbox" aria-multiline="true" aria-readonly="true" aria-label="title.js, read-only" tabindex="0">
<pre class="code-editor-pre"><span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">1</span><span class="code-editor-text"><span class="code-editor-cm">// Compose the window title of the focused container.</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">2</span><span class="code-editor-text"><span class="code-editor-kw">import</span> <span class="code-editor-punct">{</span> <span class="code-editor-type">Container</span> <span class="code-editor-punct">}</span> <span class="code-editor-kw">from</span> <span class="code-editor-str">"./tree.js"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">3</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-const">MAX_LENGTH</span> <span class="code-editor-op">=</span> <span class="code-editor-num">48</span><span class="code-editor-punct">;</span><span class="code-editor-ws" aria-hidden="true">··</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">4</span><span class="code-editor-text"><span class="code-editor-kw">export</span> <span class="code-editor-kw">function</span> <span class="code-editor-fn">title</span><span class="code-editor-punct code-editor-bracket">(</span><span class="code-editor-var">container</span><span class="code-editor-punct code-editor-bracket">)</span> <span class="code-editor-punct">{</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">5</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">const</span> <span class="code-editor-var">name</span> <span class="code-editor-op">=</span> <span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">name</span> <span class="code-editor-op">??</span> <span class="code-editor-str">"untitled<span class="code-editor-esc">…</span>"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line code-editor-line-current"><span class="code-editor-ln" aria-hidden="true">6</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">let</span> <span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-str">`${</span><span class="code-editor-var">name</span><span class="code-editor-str">} (${</span><span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">children</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span><span class="code-editor-str">})`</span><span class="code-editor-punct">;</span><span class="code-editor-caret" aria-hidden="true"></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">7</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">if</span> <span class="code-editor-punct">(</span><span class="code-editor-sel"><span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span> <span class="code-editor-op">></span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span> <span class="code-editor-punct">{</span></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">8</span><span class="code-editor-text"><span class="code-editor-sel"><span class="code-editor-indent"> </span><span class="code-editor-indent"> </span><span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-fn">slice</span><span class="code-editor-punct">(</span><span class="code-editor-num">0</span><span class="code-editor-punct">,</span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span></span> <span class="code-editor-op">+</span> <span class="code-editor-str">"…"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">9</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">10</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">return</span> <span class="code-editor-fn code-editor-deprecated">escape</span><span class="code-editor-punct">(</span><span class="code-editor-var">label</span><span class="code-editor-punct">);</span> <span class="code-editor-cm">/* escape() is deprecated */</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">11</span><span class="code-editor-text"><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">12</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-var">broken</span> <span class="code-editor-op">=</span> <span class="code-editor-num">0x1</span><span class="code-editor-invalid">G</span><span class="code-editor-punct">;</span></span></span></pre>
</div>
</div>
</div>
</div></section>
<section role="tabpanel" id="example-i3-window-frame-tabbed-workspace-panel-1" aria-labelledby="example-i3-window-frame-tabbed-workspace-tab-1" hidden=""><p>Workspace 2 is empty.</p></section><section role="tabpanel" id="example-i3-window-frame-tabbed-workspace-panel-2" aria-labelledby="example-i3-window-frame-tabbed-workspace-tab-2" hidden=""><p>Workspace 3 is empty.</p></section></div></j3w1-i3-window-frame>floating
~/theme main ls -l ports total 5 drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 alacritty drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 gtk -rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 README.md -rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 verify.sh lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 xresources -> ../tokens/ -rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 evidence.tar.gz ~/theme main cat ports/gtk/port.json cat: ports/gtk/port.json: No such file or directory ~/theme main npm run validate
// Compose the window title of the focused container. import { Container } from "./tree.js"; const MAX_LENGTH = 48; export function title(container) { const name = container.name ?? "untitled…"; let label = `${name} (${container.children.length})`; if (label.length > MAX_LENGTH) { label = label.slice(0, MAX_LENGTH) + "…"; } return escape(label); /* escape() is deprecated */ } const broken = 0x1G;
Workspace 2 is empty.
Workspace 3 is empty.
<j3w1-i3-window-frame id="example-i3-window-frame-floating"><div class="i3-window-frame" role="region" aria-label="Workstation, workspace 1: theme, with a floating window">
<div class="i3-window-frame-bar" role="group" aria-label="i3 bar">
<div class="i3-window-frame-workspaces" role="tablist" aria-label="Workspaces">
<button class="i3-window-frame-workspace" type="button" role="tab" aria-selected="true" tabindex="0" id="example-i3-window-frame-floating-workspace-tab-0" aria-controls="example-i3-window-frame-floating-workspace-panel-0">1:theme</button>
<button class="i3-window-frame-workspace i3-window-frame-workspace-demo-target" type="button" role="tab" aria-selected="false" tabindex="-1" id="example-i3-window-frame-floating-workspace-tab-1" aria-controls="example-i3-window-frame-floating-workspace-panel-1">2:reference</button>
<button class="i3-window-frame-workspace i3-window-frame-workspace-urgent" type="button" role="tab" aria-selected="false" tabindex="-1" id="example-i3-window-frame-floating-workspace-tab-2" aria-controls="example-i3-window-frame-floating-workspace-panel-2">3:ports<span class="i3-window-frame-urgent-mark">!<span class="sr-only"> urgent</span></span></button>
</div>
<div class="i3-window-frame-status" role="group" lang="zh" aria-label="状态">
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 处理器 12%</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 内存储器 3.1G</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 局域网 up</span>
<span class="i3-window-frame-block"><span class="i3-window-frame-glyph" aria-hidden="true"></span> 电池 87%</span>
<time class="i3-window-frame-block i3-window-frame-clock" datetime="2026-09-06T14:32:07">09月06号 14时32分07秒</time>
</div>
</div>
<section role="tabpanel" id="example-i3-window-frame-floating-workspace-panel-0" aria-labelledby="example-i3-window-frame-floating-workspace-tab-0"><div class="i3-window-frame-area i3-window-frame-area-floating">
<div class="i3-window-frame-window" role="group" aria-label="Terminal window" tabindex="0">
<div class="i3-window-frame-title">~/theme — zsh</div>
<pre class="terminal terminal-transcript" role="log" aria-label="Shell transcript" tabindex="0"><span class="terminal-line"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> ls -l ports</span>
<span class="terminal-line">total 5</span>
<span class="terminal-line">drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 <span class="terminal-fg-5 terminal-bold">alacritty</span></span>
<span class="terminal-line">drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 <span class="terminal-fg-5 terminal-bold">gtk</span></span>
<span class="terminal-line">-rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 <span class="terminal-sel">README.md</span></span>
<span class="terminal-line"><span class="terminal-sel">-rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 </span><span class="terminal-sel terminal-fg-3">verify.sh</span></span>
<span class="terminal-line">lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 <span class="terminal-fg-7">xresources</span> -> <span class="terminal-fg-2">../tokens/</span></span>
<span class="terminal-line">-rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 <span class="terminal-fg-11">evidence.tar.gz</span></span>
<span class="terminal-line"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> cat ports/gtk/port.json</span>
<span class="terminal-line terminal-fg-5">cat: ports/gtk/port.json: No such file or directory</span>
<span class="terminal-line terminal-line-cursor"><span class="terminal-prompt"><span class="terminal-prompt-path"> ~/theme </span><span class="terminal-prompt-arrow terminal-prompt-arrow-path" aria-hidden="true">▶</span><span class="terminal-prompt-branch"> main </span><span class="terminal-prompt-arrow terminal-prompt-arrow-branch" aria-hidden="true">▶</span></span> npm run validate<span class="terminal-cursor" aria-hidden="true"> </span></span></pre>
</div>
<div class="i3-window-frame-window i3-window-frame-floating i3-window-frame-focused" role="group" aria-label="Editor window, floating, focused" tabindex="0">
<div class="i3-window-frame-title">title.js — editor</div>
<div class="code-editor" role="textbox" aria-multiline="true" aria-readonly="true" aria-label="title.js, read-only" tabindex="0">
<pre class="code-editor-pre"><span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">1</span><span class="code-editor-text"><span class="code-editor-cm">// Compose the window title of the focused container.</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">2</span><span class="code-editor-text"><span class="code-editor-kw">import</span> <span class="code-editor-punct">{</span> <span class="code-editor-type">Container</span> <span class="code-editor-punct">}</span> <span class="code-editor-kw">from</span> <span class="code-editor-str">"./tree.js"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">3</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-const">MAX_LENGTH</span> <span class="code-editor-op">=</span> <span class="code-editor-num">48</span><span class="code-editor-punct">;</span><span class="code-editor-ws" aria-hidden="true">··</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">4</span><span class="code-editor-text"><span class="code-editor-kw">export</span> <span class="code-editor-kw">function</span> <span class="code-editor-fn">title</span><span class="code-editor-punct code-editor-bracket">(</span><span class="code-editor-var">container</span><span class="code-editor-punct code-editor-bracket">)</span> <span class="code-editor-punct">{</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">5</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">const</span> <span class="code-editor-var">name</span> <span class="code-editor-op">=</span> <span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">name</span> <span class="code-editor-op">??</span> <span class="code-editor-str">"untitled<span class="code-editor-esc">…</span>"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line code-editor-line-current"><span class="code-editor-ln" aria-hidden="true">6</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">let</span> <span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-str">`${</span><span class="code-editor-var">name</span><span class="code-editor-str">} (${</span><span class="code-editor-var">container</span><span class="code-editor-punct">.</span><span class="code-editor-prop">children</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span><span class="code-editor-str">})`</span><span class="code-editor-punct">;</span><span class="code-editor-caret" aria-hidden="true"></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">7</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">if</span> <span class="code-editor-punct">(</span><span class="code-editor-sel"><span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-prop">length</span> <span class="code-editor-op">></span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span> <span class="code-editor-punct">{</span></span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">8</span><span class="code-editor-text"><span class="code-editor-sel"><span class="code-editor-indent"> </span><span class="code-editor-indent"> </span><span class="code-editor-var">label</span> <span class="code-editor-op">=</span> <span class="code-editor-var">label</span><span class="code-editor-punct">.</span><span class="code-editor-fn">slice</span><span class="code-editor-punct">(</span><span class="code-editor-num">0</span><span class="code-editor-punct">,</span> <span class="code-editor-const">MAX_LENGTH</span><span class="code-editor-punct">)</span></span> <span class="code-editor-op">+</span> <span class="code-editor-str">"…"</span><span class="code-editor-punct">;</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">9</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">10</span><span class="code-editor-text"><span class="code-editor-indent"> </span><span class="code-editor-kw">return</span> <span class="code-editor-fn code-editor-deprecated">escape</span><span class="code-editor-punct">(</span><span class="code-editor-var">label</span><span class="code-editor-punct">);</span> <span class="code-editor-cm">/* escape() is deprecated */</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">11</span><span class="code-editor-text"><span class="code-editor-punct">}</span></span></span>
<span class="code-editor-line"><span class="code-editor-ln" aria-hidden="true">12</span><span class="code-editor-text"><span class="code-editor-kw">const</span> <span class="code-editor-var">broken</span> <span class="code-editor-op">=</span> <span class="code-editor-num">0x1</span><span class="code-editor-invalid">G</span><span class="code-editor-punct">;</span></span></span></pre>
</div>
</div>
</div></section>
<section role="tabpanel" id="example-i3-window-frame-floating-workspace-panel-1" aria-labelledby="example-i3-window-frame-floating-workspace-tab-1" hidden=""><p>Workspace 2 is empty.</p></section><section role="tabpanel" id="example-i3-window-frame-floating-workspace-panel-2" aria-labelledby="example-i3-window-frame-floating-workspace-tab-2" hidden=""><p>Workspace 3 is empty.</p></section></div></j3w1-i3-window-frame>Install and register
Use the exact version. If npm publication is pending, install the release tarball from the installation guide.
npm install --save-exact @j3w1/ui@1.1.0import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/i3-window-frame.css';
import '@j3w1/ui/register/i3-window-frame';Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.
Copy the complete implementation
npx --package=@j3w1/ui@1.1.0 j3w1-ui copy i3-window-frame --out ./vendor/j3w1/i3-window-frameThe CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.
Public API
attributes
| Name | Type / signature | Meaning |
|---|---|---|
disabled | CustomEvent | Disable the native controls owned by this instance. |
loading | CustomEvent | Mark the primary control busy and prevent duplicate activation. |
properties
| Name | Type / signature | Meaning |
|---|---|---|
disabled | boolean | Delegates disabled state to native children. |
name | string | Native control name; application form keys remain explicit. |
methods
| Name | Type / signature | Meaning |
|---|---|---|
refresh | (): void | Reconnect behavior after replacing native child structure. |
focus | (options?: FocusOptions): void | Focus the first usable native control. |
events
| Name | Type / signature | Meaning |
|---|---|---|
j3w1-window-action | CustomEvent | Request a host window action; the browser component does not control OS windows. |
Dependencies and verification
Registered components: none. Style dependencies: terminal, code-editor, tabs.
Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.
Full implementation manifest, API and token dependency closure
Canonical specification
Purpose
The frame the palette was tuned in: an i3 bar, tiled windows with 1px pixel borders, 14/−2 gaps, a tabbed container and one floating window. It proves that the terminal and code-editor components sit inside window chrome drawn from the same roles, and it records the workstation’s geometry as reference metrics. It is a specimen of composition, not a desktop.
Anatomy
The region on color.surface.desktop; the 28px bar on
color.surface.chrome with the workspace tablist (the visible workspace on
color.interaction.selection.bg) and the i3status group in Chinese with
its clock in color.text.bright; the workspace grid with the reference
gaps; each window a focusable group behind a 1px color.border.default
frame, color.border.active when focused, with a 23px title bar in
color.text.muted on color.surface.chrome that turns color.text.bright
on color.surface.chrome-alt when focused; the terminal or code-editor as
the client with its own boundary given up to the frame. Tabbed: the titles
form a tablist whose visible tab carries the 2px
color.border.selected-indicator. Floating: the editor sits above the
terminal behind color.border.overlay with shadow.floating, the only
shadow in the theme.
States
| State | Visual | Non-colour channel |
|---|---|---|
| default | the terminal window focused: color.border.active frame and the bright title; the editor at rest; workspace 1 filled | the title text; aria-selected |
| focus-visible | the 2px color.interaction.focus.ring-container ring at −3px inside the focused window or container; the client draws no second ring | the ring |
| selected | workspace 2 takes the selection fill with color.interaction.selection.text; workspace 1 drops to rest | aria-selected |
| container-inactive | no window has focus: every frame color.border.default, every title color.text.muted on color.surface.chrome, the tab indicator color.border.selected-indicator-inactive, no caret | lightness drop; no caret |
| error | workspace 3 is urgent: color.status.danger.fill with color.status.danger.on-fill and a trailing ! | the ! mark; “urgent” read to assistive technology |
The clients keep their own states (the terminal’s busy cursor, the editor’s selection and read-only gutter); this specimen declares only the states the frame adds around them.
Keyboard
The workspace tablist is one stop with arrow movement; Enter or Space shows a workspace. Each window is a focusable group so the frame itself can hold focus, as i3 focuses a container; Tab then enters the client, whose own contract applies (the terminal log, the read-only editor textbox). In the tabbed variant the tab list is one stop and only the visible panel’s client follows. Mod+h j k l and Mod+1..9 belong to the window manager and are documented, not reimplemented. Nothing traps focus.
Accessibility
The region names its workspace and layout. The bar is a group holding a
named tablist and a status group in lang="zh", whose Nerd Font glyphs
are aria-hidden so only the labels are read. Every window is a named
group; the focused one says so in its name and the floating one too.
Urgency carries the ! mark and a visually hidden “urgent”. Contrast:
workspace names 8.60:1 and the clock 10.31:1 on the bar, focused titles
10.01:1, rest titles 5.77:1, the focused frame 4.97:1 and the ring 10.37:1
on the window surface, the urgent fill 4.12:1 against the bar with 4.58:1
text on it.
Portability
Groups, a tablist and the clients’ own roles; every state renders from the root hooks without a script. The i3 geometry — bar 28px, titles 23px, gaps 14px inner and −2px outer — is reproduced with private variables on this specimen’s root only. Those numbers are reference metrics of the workstation, recorded so a port of the i3 configuration matches it; they are not tokens and not a rule for any other application, which keeps its own window manager, bar and gaps. The specimen proves composition, not a product; only i3 and sway ports draw this frame.
Non-examples
A window with rounded corners or a title bar gradient. A focused window that glows or blurs its neighbours. A shadow under a tiled window; the only shadow is the floating one. Gaps of 14/−2 forced into an unrelated application’s layout. A workspace indicator drawn as a red underline instead of the selection fill. Traffic-light close buttons in the title bar. A translucent bar.