regular-layout 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,11 +35,11 @@
35
35
  export declare class RegularLayoutFrame extends HTMLElement {
36
36
  private _shadowRoot;
37
37
  private _container_sheet;
38
- private _title_sheet;
38
+ private _tab_sheet;
39
39
  private _layout;
40
40
  private _header;
41
+ private _default_tab;
41
42
  private _drag;
42
- private _tab_to_index_map;
43
43
  /**
44
44
  * Initializes this elements. Override this method and
45
45
  * `disconnectedCallback` to modify how this subclass renders the Shadow
@@ -57,14 +57,16 @@ export declare class RegularLayoutFrame extends HTMLElement {
57
57
  private onPointerLost;
58
58
  private drawTabs;
59
59
  /**
60
- * Regenerates this frame's title stylesheet, mapping each tab (by its slot
61
- * `name`) to its label. The label is rendered via the title's `::before`
62
- * `content`, reading the slot's `--regular-layout-<slot>--title` override
63
- * variable with the slot name as the fallback - so a consumer can relabel a
64
- * tab purely in CSS and the change applies reactively.
60
+ * Regenerates this frame's tab stylesheet: the shared titlebar grid template
61
+ * (so every frame in the stack aligns), the column this frame's tab occupies,
62
+ * and the tab label. The label renders via the title's `::before` `content`,
63
+ * reading the slot's `--regular-layout-<slot>--title` override variable with
64
+ * the slot name as the fallback - so a consumer can relabel a tab purely in
65
+ * CSS and the change applies reactively.
65
66
  *
66
- * @param attr - The child name attribute (`CHILD_ATTRIBUTE_NAME`).
67
- * @param tabs - The slot names rendered in this frame's titlebar.
67
+ * @param slot - This frame's panel name.
68
+ * @param index - This frame's column (zero-based) within the stack.
69
+ * @param count - The number of tabs in the stack.
68
70
  */
69
- private drawTitles;
71
+ private drawTab;
70
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "regular-layout",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A regular CSS `grid` container",
5
5
  "keywords": [],
6
6
  "license": "Apache-2.0",
@@ -18,6 +18,7 @@ interface GridCell {
18
18
  colEnd: number;
19
19
  rowStart: number;
20
20
  rowEnd: number;
21
+ zIndex?: number;
21
22
  }
22
23
 
23
24
  function dedupe_sort(physics: Physics, result: number[], pos: number) {
@@ -102,15 +103,26 @@ function build_cells(
102
103
  ): GridCell[] {
103
104
  if (panel.type === "tab-layout") {
104
105
  const selected = panel.selected ?? 0;
105
- return [
106
- {
107
- child: panel.tabs[selected],
108
- colStart: find_track_index(physics, colPositions, colStart),
109
- colEnd: find_track_index(physics, colPositions, colEnd),
110
- rowStart: find_track_index(physics, rowPositions, rowStart),
111
- rowEnd: find_track_index(physics, rowPositions, rowEnd),
112
- },
113
- ];
106
+ const col_start = find_track_index(physics, colPositions, colStart);
107
+ const col_end = find_track_index(physics, colPositions, colEnd);
108
+ const row_start = find_track_index(physics, rowPositions, rowStart);
109
+ const row_end = find_track_index(physics, rowPositions, rowEnd);
110
+
111
+ // Stacked tabs all occupy the same cell, overlapping. Only the selected
112
+ // frame is lifted (`z-index:1`) so its content sits on top; the rest
113
+ // self-hide their content and just contribute their tab. Keeping the
114
+ // max frame `z-index` at 1 lets the drag overlay sit above any stack
115
+ // with a small constant (2). A single-tab stack
116
+ // emits one placement with no `z-index`, so non-stacked output is
117
+ // unchanged.
118
+ return panel.tabs.map((child, i) => ({
119
+ child,
120
+ colStart: col_start,
121
+ colEnd: col_end,
122
+ rowStart: row_start,
123
+ rowEnd: row_end,
124
+ zIndex: panel.tabs.length > 1 && i === selected ? 1 : undefined,
125
+ }));
114
126
  }
115
127
 
116
128
  const { children, sizes, orientation } = panel;
@@ -162,8 +174,11 @@ const child_template = (
162
174
  slot: string,
163
175
  rowPart: string,
164
176
  colPart: string,
177
+ zIndex?: number,
165
178
  ) =>
166
- `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:flex;grid-column:${colPart};grid-row:${rowPart}}`;
179
+ `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){display:flex;grid-column:${colPart};grid-row:${rowPart}${
180
+ zIndex === undefined ? "" : `;z-index:${zIndex}`
181
+ }}`;
167
182
 
168
183
  /**
169
184
  * Generates CSS Grid styles to render a layout tree.
@@ -202,9 +217,18 @@ export function create_css_grid_layout(
202
217
  ): string {
203
218
  if (layout.type === "tab-layout") {
204
219
  const selected = layout.selected ?? 0;
220
+ const stacked = layout.tabs.length > 1;
205
221
  return [
206
222
  host_template("100%", "100%"),
207
- child_template(physics, layout.tabs[selected], "1", "1"),
223
+ ...layout.tabs.map((tab, i) =>
224
+ child_template(
225
+ physics,
226
+ tab,
227
+ "1",
228
+ "1",
229
+ stacked && i === selected ? 1 : undefined,
230
+ ),
231
+ ),
208
232
  ].join("\n");
209
233
  }
210
234
 
@@ -253,11 +277,13 @@ export function create_css_grid_layout(
253
277
  for (const cell of cells) {
254
278
  const colPart = formatGridLine(cell.colStart, cell.colEnd);
255
279
  const rowPart = formatGridLine(cell.rowStart, cell.rowEnd);
256
- css.push(child_template(physics, cell.child, rowPart, colPart));
280
+ css.push(
281
+ child_template(physics, cell.child, rowPart, colPart, cell.zIndex),
282
+ );
257
283
  if (cell.child === overlay?.[1]) {
258
284
  css.push(child_template(physics, overlay[0], rowPart, colPart));
259
285
  css.push(
260
- `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}=${overlay[0]}]){z-index:1}`,
286
+ `:host ::slotted([${physics.CHILD_ATTRIBUTE_NAME}=${overlay[0]}]){z-index:2}`,
261
287
  );
262
288
  }
263
289
  }
@@ -69,6 +69,6 @@ export function updateOverlaySheet(
69
69
  margin,
70
70
  );
71
71
 
72
- const css = `display:flex;position:absolute!important;z-index:1;top:${local.y}px;left:${local.x}px;height:${local.height}px;width:${local.width}px;`;
72
+ const css = `display:flex;position:absolute!important;z-index:2;top:${local.y}px;left:${local.x}px;height:${local.height}px;width:${local.width}px;`;
73
73
  return `::slotted([${physics.CHILD_ATTRIBUTE_NAME}="${slot}"]){${css}}`;
74
74
  }
@@ -15,17 +15,19 @@ import type { RegularLayout } from "./regular-layout.ts";
15
15
  import type { RegularLayoutTab } from "./regular-layout-tab.ts";
16
16
 
17
17
  const CSS = `
18
- :host{box-sizing:border-box;flex-direction:column}
19
- :host::part(titlebar){display:flex;height:24px;user-select:none;overflow:hidden}
20
- :host::part(container){flex:1 1 auto}
21
- :host::part(title){flex:1 1 auto;pointer-events:none}
22
- :host::part(close){align-self:stretch}
23
- :host::slotted{flex:1 1 auto;}
24
- :host regular-layout-tab{width:0px;}
18
+ :host{box-sizing:border-box;flex-direction:column;pointer-events:none}
19
+ [part~="titlebar"]{height:24px;user-select:none;overflow:hidden}
20
+ [part~="container"]{flex:1 1 auto;pointer-events:auto}
21
+ [part~="title"]{flex:1 1 auto;pointer-events:none}
22
+ [part~="close"]{align-self:stretch}
23
+ :host([inactive]) [part~="container"]{display:none}
24
+ .tabs{display:grid;width:100%;height:100%}
25
+ .tabs slot[name="tab"]{display:grid;grid-row:1;pointer-events:auto}
26
+ .tabs regular-layout-tab{display:flex;overflow:hidden}
25
27
  `;
26
28
 
27
29
  const HTML_TEMPLATE = `
28
- <div part="titlebar"></div>
30
+ <div part="titlebar"><div class="tabs"><slot name="tab"><regular-layout-tab></regular-layout-tab></slot></div></div>
29
31
  <div part="container"><slot></slot></div>
30
32
  `;
31
33
 
@@ -82,11 +84,11 @@ const title_variable = (slot: string): string =>
82
84
  export class RegularLayoutFrame extends HTMLElement {
83
85
  private _shadowRoot!: ShadowRoot;
84
86
  private _container_sheet!: CSSStyleSheet;
85
- private _title_sheet!: CSSStyleSheet;
87
+ private _tab_sheet!: CSSStyleSheet;
86
88
  private _layout!: RegularLayout;
87
89
  private _header!: HTMLElement;
90
+ private _default_tab!: RegularLayoutTab;
88
91
  private _drag: DragState | null = null;
89
- private _tab_to_index_map: WeakMap<RegularLayoutTab, number> = new WeakMap();
90
92
 
91
93
  /**
92
94
  * Initializes this elements. Override this method and
@@ -96,15 +98,20 @@ export class RegularLayoutFrame extends HTMLElement {
96
98
  connectedCallback() {
97
99
  this._container_sheet ??= new CSSStyleSheet();
98
100
  this._container_sheet.replaceSync(CSS);
99
- this._title_sheet ??= new CSSStyleSheet();
101
+ this._tab_sheet ??= new CSSStyleSheet();
100
102
  this._shadowRoot ??= this.attachShadow({ mode: "open" });
101
103
  this._shadowRoot.adoptedStyleSheets = [
102
104
  this._container_sheet,
103
- this._title_sheet,
105
+ this._tab_sheet,
104
106
  ];
105
107
  this._shadowRoot.innerHTML = HTML_TEMPLATE;
106
108
  this._layout = this.parentElement as RegularLayout;
107
109
  this._header = this._shadowRoot.children[0] as HTMLElement;
110
+ // The built-in tab is the `slot="tab"` fallback; a consumer-supplied
111
+ // `slot="tab"` child replaces it.
112
+ this._default_tab = this._header.querySelector(
113
+ "regular-layout-tab",
114
+ ) as RegularLayoutTab;
108
115
  this._header.addEventListener("pointerdown", this.onPointerDown);
109
116
  this.addEventListener("pointermove", this.onPointerMove);
110
117
  this.addEventListener("pointerup", this.onPointerUp);
@@ -140,9 +147,17 @@ export class RegularLayoutFrame extends HTMLElement {
140
147
 
141
148
  const elem = event.target as RegularLayoutTab;
142
149
  if (elem.part.contains("tab")) {
150
+ const slot = this.getAttribute(
151
+ this._layout.savePhysics().CHILD_ATTRIBUTE_NAME,
152
+ );
153
+
143
154
  const path = this._layout.calculateIntersect(event);
144
- if (path) {
145
- this._drag = { path };
155
+ if (path && slot) {
156
+ // Drag *this frame's* panel, not whichever panel is front-most at
157
+ // the pointer (which is what `calculateIntersect` resolves to in a
158
+ // stack). The overlapping stack shares geometry, so only the slot
159
+ // needs overriding.
160
+ this._drag = { path: { ...path, slot } };
146
161
  this.setPointerCapture(event.pointerId);
147
162
  event.preventDefault();
148
163
  } else {
@@ -190,54 +205,41 @@ export class RegularLayoutFrame extends HTMLElement {
190
205
  return;
191
206
  }
192
207
 
193
- const new_panel = event.detail;
194
- let new_tab_panel = this._layout.getPanel(slot, new_panel);
195
- if (!new_tab_panel) {
196
- new_tab_panel = {
197
- type: "tab-layout",
198
- tabs: [slot],
199
- selected: 0,
200
- };
201
- }
202
-
203
- for (let i = 0; i < new_tab_panel.tabs.length; i++) {
204
- if (i >= this._header.children.length) {
205
- const new_tab = document.createElement("regular-layout-tab");
206
- new_tab.populate(this._layout, new_tab_panel, i);
207
- this._header.appendChild(new_tab);
208
- this._tab_to_index_map.set(new_tab, i);
209
- } else {
210
- const tab = this._header.children[i] as RegularLayoutTab;
211
- tab.populate(this._layout, new_tab_panel, i);
212
- }
213
- }
214
-
215
- const last_index = new_tab_panel.tabs.length;
216
- for (let j = this._header.children.length - 1; j >= last_index; j--) {
217
- this._header.removeChild(this._header.children[j]);
208
+ let tab_panel = this._layout.getPanel(slot, event.detail);
209
+ if (!tab_panel) {
210
+ tab_panel = { type: "tab-layout", tabs: [slot], selected: 0 };
218
211
  }
219
212
 
220
- this.drawTitles(attr, new_tab_panel.tabs);
213
+ // Each frame renders only its *own* tab. Frames in a stack overlap (see
214
+ // `create_css_grid_layout`); the tabs tile because every frame derives
215
+ // the same column template and places its tab in its own column. Only
216
+ // the selected frame shows its content; the rest keep just their tab.
217
+ const index = tab_panel.tabs.indexOf(slot);
218
+ const selected = (tab_panel.selected ?? 0) === index;
219
+ this.toggleAttribute("inactive", !selected);
220
+ this._default_tab.populate(this._layout, tab_panel, index);
221
+ this.drawTab(slot, index, tab_panel.tabs.length);
221
222
  };
222
223
 
223
224
  /**
224
- * Regenerates this frame's title stylesheet, mapping each tab (by its slot
225
- * `name`) to its label. The label is rendered via the title's `::before`
226
- * `content`, reading the slot's `--regular-layout-<slot>--title` override
227
- * variable with the slot name as the fallback - so a consumer can relabel a
228
- * tab purely in CSS and the change applies reactively.
225
+ * Regenerates this frame's tab stylesheet: the shared titlebar grid template
226
+ * (so every frame in the stack aligns), the column this frame's tab occupies,
227
+ * and the tab label. The label renders via the title's `::before` `content`,
228
+ * reading the slot's `--regular-layout-<slot>--title` override variable with
229
+ * the slot name as the fallback - so a consumer can relabel a tab purely in
230
+ * CSS and the change applies reactively.
229
231
  *
230
- * @param attr - The child name attribute (`CHILD_ATTRIBUTE_NAME`).
231
- * @param tabs - The slot names rendered in this frame's titlebar.
232
+ * @param slot - This frame's panel name.
233
+ * @param index - This frame's column (zero-based) within the stack.
234
+ * @param count - The number of tabs in the stack.
232
235
  */
233
- private drawTitles = (attr: string, tabs: string[]) => {
234
- const css = tabs
235
- .map(
236
- (slot) =>
237
- `regular-layout-tab[${attr}="${slot}"] [part~="title"]::before{content:var(${title_variable(slot)}, ${css_string(slot)})}`,
238
- )
239
- .join("\n");
240
-
241
- this._title_sheet.replaceSync(css);
236
+ private drawTab = (slot: string, index: number, count: number) => {
237
+ this._tab_sheet.replaceSync(
238
+ [
239
+ `.tabs{grid-template-columns:repeat(${count},var(--rl-tab-width,1fr))}`,
240
+ `.tabs slot[name="tab"]{grid-column:${index + 1}}`,
241
+ `[part~="title"]::before{content:var(${title_variable(slot)}, ${css_string(slot)})}`,
242
+ ].join("\n"),
243
+ );
242
244
  };
243
245
  }
@@ -23,12 +23,15 @@ regular-layout.borland regular-layout-frame {
23
23
  position: relative;
24
24
  box-sizing: border-box;
25
25
  margin: 4px;
26
- background: #0000aa;
27
- border: 1px solid #00aaaa;
28
- box-shadow: 1px 1px 0 #000000;
29
26
  }
30
27
 
28
+ /* Panel chrome lives on the content (only the selected frame shows it), so the
29
+ overlapping frame box stays transparent and every stacked tab is visible. */
31
30
  regular-layout.borland regular-layout-frame::part(container) {
31
+ background: #0000aa;
32
+ border: 1px solid #00aaaa;
33
+ box-shadow: 1px 1px 0 #000000;
34
+ box-sizing: border-box;
32
35
  padding: 4px 6px;
33
36
  color: #ffff55;
34
37
  }
@@ -59,7 +62,6 @@ regular-layout.borland regular-layout-frame::part(titlebar) {
59
62
  align-items: stretch;
60
63
  padding-right: 0;
61
64
  height: 22px;
62
- background: #0000aa;
63
65
  }
64
66
 
65
67
  regular-layout.borland regular-layout-frame::part(tab) {
@@ -23,13 +23,16 @@ regular-layout.chicago regular-layout-frame {
23
23
  position: relative;
24
24
  box-sizing: border-box;
25
25
  margin: 12px;
26
+ }
27
+
28
+ /* Panel chrome lives on the content (only the selected frame shows it), so the
29
+ overlapping frame box stays transparent and every stacked tab is visible. */
30
+ regular-layout.chicago regular-layout-frame::part(container) {
26
31
  background: #c0c0c0;
27
32
  border-width: 2px;
28
33
  border-color: #ffffff #808080 #808080 #ffffff;
29
34
  border-style: solid;
30
- }
31
-
32
- regular-layout.chicago regular-layout-frame::part(container) {
35
+ box-sizing: border-box;
33
36
  padding: 6px;
34
37
  }
35
38
 
@@ -58,8 +61,8 @@ regular-layout.chicago regular-layout-frame::part(titlebar) {
58
61
  }
59
62
 
60
63
  regular-layout.chicago regular-layout-frame::part(tab) {
64
+ box-sizing: border-box;
61
65
  display: flex;
62
- flex: 1 1 150px;
63
66
  align-items: center;
64
67
  padding: 0 3px 0 8px;
65
68
  cursor: pointer;
@@ -69,6 +72,11 @@ regular-layout.chicago regular-layout-frame::part(tab) {
69
72
  font-family: "Tahoma", "Arial", sans-serif;
70
73
  font-weight: bold;
71
74
  font-size: 11px;
75
+ /* Raised bevel - the frame box no longer carries the border in the
76
+ overlap model, so each tab frames itself. */
77
+ border-width: 2px;
78
+ border-color: #ffffff #808080 #808080 #ffffff;
79
+ border-style: solid;
72
80
  }
73
81
 
74
82
  regular-layout.chicago regular-layout-frame::part(active-tab) {
@@ -21,13 +21,16 @@ regular-layout.fluxbox regular-layout-frame {
21
21
  position: relative;
22
22
  box-sizing: border-box;
23
23
  margin: 8px;
24
- background: #ffffff;
25
- border: 1px solid #9dacbe;
26
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15);
27
24
  }
28
25
 
26
+ /* Panel chrome lives on the content (only the selected frame shows it), so the
27
+ overlapping frame box stays transparent and every stacked tab is visible. */
29
28
  regular-layout.fluxbox regular-layout-frame::part(container) {
29
+ box-sizing: border-box;
30
30
  padding: 6px;
31
+ background: #ffffff;
32
+ border: 1px solid #9dacbe;
33
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15);
31
34
  }
32
35
 
33
36
  regular-layout.fluxbox regular-layout-frame::part(close) {
package/themes/gibson.css CHANGED
@@ -20,12 +20,9 @@ regular-layout.gibson {
20
20
 
21
21
  /* Frame */
22
22
  regular-layout.gibson regular-layout-frame {
23
- --titlebar-height: 28px;
24
23
  position: relative;
25
24
  box-sizing: border-box;
26
25
  margin: 8px;
27
- background: rgba(10, 10, 26, 0.85);
28
- backdrop-filter: blur(4px);
29
26
  color: white;
30
27
  text-shadow:
31
28
  0 0 5px #fff,
@@ -33,8 +30,13 @@ regular-layout.gibson regular-layout-frame {
33
30
  0 0 20px #fff;
34
31
  }
35
32
 
33
+ /* Panel chrome lives on the content (only the selected frame shows it), so the
34
+ overlapping frame box stays transparent and every stacked tab is visible. */
36
35
  regular-layout.gibson regular-layout-frame::part(container) {
37
36
  padding: 6px;
37
+ background: rgba(10, 10, 26, 0.85);
38
+ backdrop-filter: blur(4px);
39
+ box-sizing: border-box;
38
40
  }
39
41
 
40
42
  regular-layout.gibson regular-layout-frame::part(active-close) {
@@ -70,19 +72,11 @@ regular-layout.gibson regular-layout-frame::part(close) {
70
72
  }
71
73
 
72
74
  regular-layout.gibson regular-layout-frame::part(titlebar) {
73
- display: flex;
74
- justify-content: flex-end;
75
- align-items: stretch;
76
- padding-left: 12px;
77
75
  height: 32px;
78
-
79
- /* border-bottom: 1px solid rgba(0, 255, 255, 0.3); */
80
76
  }
81
77
 
82
78
  regular-layout.gibson regular-layout-frame::part(tab) {
83
79
  display: flex;
84
- flex: 1 1 150px;
85
- max-width: 200px;
86
80
  align-items: center;
87
81
  padding: 0 5px 0 12px;
88
82
  cursor: pointer;
@@ -131,6 +125,7 @@ regular-layout.gibson regular-layout-frame.overlay {
131
125
  inset 0 0 20px rgba(255, 0, 255, 0.2);
132
126
  margin: 0;
133
127
  transition:
128
+ margin 0.075s ease-out,
134
129
  top 0.075s ease-out,
135
130
  height 0.075s ease-out,
136
131
  width 0.075s ease-out,
@@ -138,18 +133,9 @@ regular-layout.gibson regular-layout-frame.overlay {
138
133
  }
139
134
 
140
135
  regular-layout.gibson regular-layout-frame::part(container) {
141
- display: none;
142
136
  border: 1px solid #00ffff;
143
- border-radius: 12px 0 12px 0;
144
- }
145
-
146
- regular-layout.gibson regular-layout-frame::part(titlebar) {
147
- display: none;
148
- }
149
-
150
- regular-layout.gibson regular-layout-frame:not(.overlay)::part(container),
151
- regular-layout.gibson regular-layout-frame:not(.overlay)::part(titlebar) {
152
- display: flex;
137
+ /* Square top so the tab strip sits flush; keep the asymmetric bottom. */
138
+ border-radius: 0 0 12px 0;
153
139
  }
154
140
 
155
141
  /* Cyberpunk color variations */
package/themes/hotdog.css CHANGED
@@ -21,13 +21,16 @@ regular-layout.hotdog {
21
21
  /* Frame */
22
22
  regular-layout.hotdog regular-layout-frame {
23
23
  margin: 12px;
24
+ }
25
+
26
+ /* Panel chrome lives on the content (only the selected frame shows it), so the
27
+ overlapping frame box stays transparent and every stacked tab is visible. */
28
+ regular-layout.hotdog regular-layout-frame::part(container) {
24
29
  background: #ffff00;
25
30
  border-width: 2px;
26
31
  border-color: #ff6b6b #8b0000 #8b0000 #ff6b6b;
27
32
  border-style: solid;
28
- }
29
-
30
- regular-layout.hotdog regular-layout-frame::part(container) {
33
+ box-sizing: border-box;
31
34
  padding: 6px;
32
35
  }
33
36
 
@@ -57,8 +60,8 @@ regular-layout.hotdog regular-layout-frame::part(titlebar) {
57
60
  }
58
61
 
59
62
  regular-layout.hotdog regular-layout-frame::part(tab) {
63
+ box-sizing: border-box;
60
64
  display: flex;
61
- flex: 1 1 150px;
62
65
  align-items: center;
63
66
  padding: 0 3px 0 8px;
64
67
  cursor: pointer;
@@ -68,6 +71,11 @@ regular-layout.hotdog regular-layout-frame::part(tab) {
68
71
  font-family: "Tahoma", "Arial", sans-serif;
69
72
  font-weight: bold;
70
73
  font-size: 11px;
74
+ /* Raised bevel - the frame box no longer carries the border in the
75
+ overlap model, so each tab frames itself. */
76
+ border-width: 2px;
77
+ border-color: #ff6b6b #8b0000 #8b0000 #ff6b6b;
78
+ border-style: solid;
71
79
  }
72
80
 
73
81
  regular-layout.hotdog regular-layout-frame::part(active-tab) {
@@ -87,6 +95,7 @@ regular-layout.hotdog:has(.overlay) > .overlay {
87
95
  regular-layout.hotdog regular-layout-frame.overlay {
88
96
  margin: 0;
89
97
  transition:
98
+ margin 0.1 ease-in-out,
90
99
  top 0.1s ease-in-out,
91
100
  height 0.1s ease-in-out,
92
101
  width 0.1s ease-in-out,