@cplieger/web-terminal-ui 3.5.0 → 4.0.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.
- package/README.md +52 -23
- package/css/00-tokens.css +8 -2
- package/css/01-scope.css +56 -0
- package/css/{02-app.css → 02-terminal.css} +64 -239
- package/css/{03-primitives.css → 10-primitives.css} +21 -19
- package/css/20-connection-banner.css +42 -0
- package/css/21-scroll-to-bottom.css +65 -0
- package/css/22-context-menu.css +45 -0
- package/css/23-toolbar.css +181 -0
- package/css/{05-tabs.css → 30-tabs.css} +96 -57
- package/css/{06-mobile.css → 31-switcher.css} +66 -81
- package/css/{07-animations.css → 40-animations.css} +15 -15
- package/css/MANIFEST +19 -11
- package/css/MANIFEST.single +12 -0
- package/css/MANIFEST.tabbed +16 -0
- package/css/MANIFEST.touch +12 -0
- package/css/{01-reset.css → page.css} +18 -19
- package/package.json +18 -5
- package/scaffold/index.html +5 -0
- package/src/features/context-menu.ts +6 -33
- package/src/features/menu-position.ts +50 -0
- package/src/features/{tabs.ts → tabs/index.ts} +204 -655
- package/src/features/tabs/model.ts +334 -0
- package/src/features/tabs/strip.ts +101 -0
- package/src/features/tabs/switcher.ts +129 -0
- package/src/index.ts +1 -0
- package/src/kernel/a11y.ts +30 -5
- package/src/kernel/kernel.ts +149 -59
- package/src/kernel/types.ts +55 -14
- package/src/presets/agent-tabbed.ts +15 -0
- package/src/presets/single.ts +27 -0
- package/src/presets/tabbed.ts +42 -0
- package/src/presets/touch.ts +13 -0
- package/src/presets.ts +12 -76
- package/src/viewport.ts +28 -15
- package/css/04-feedback.css +0 -180
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
/* ===== web-terminal-ui design system: primitives =====
|
|
2
|
-
The
|
|
3
|
-
terminal surface (.term, styled in 02-
|
|
4
|
-
region slots. The regions are transparent groupings (display: contents)
|
|
5
|
-
each feature's element floats over the
|
|
6
|
-
feature contributes nothing.
|
|
2
|
+
The kernel builds named layout regions (wt-region-*) as siblings of the
|
|
3
|
+
terminal surface (.term, styled in 02-terminal.css); features mount chrome
|
|
4
|
+
into region slots. The regions are transparent groupings (display: contents)
|
|
5
|
+
so each feature's element floats over the root-filling terminal, and an
|
|
6
|
+
absent feature contributes nothing.
|
|
7
7
|
|
|
8
|
-
The stylesheet is layered by concern and concatenated in
|
|
9
|
-
tokens (00) ->
|
|
10
|
-
shared primitives (
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
The stylesheet is layered by concern and concatenated in manifest order:
|
|
9
|
+
tokens (00) -> scope + layout modes (01) -> terminal core (02) -> these
|
|
10
|
+
shared primitives (10) -> per-feature chrome (20-23, 30-31, one file per
|
|
11
|
+
feature so a per-preset manifest ships only what its composition mounts) ->
|
|
12
|
+
animations (40, where every .wt-animate-gated transition/keyframe lives, so
|
|
13
|
+
motion is opt-in from one place). Cascade order is the manifest file order.
|
|
13
14
|
|
|
14
15
|
Primitives are the building blocks every feature reuses: the transparent
|
|
15
16
|
layout regions, the 44px touch button, and the toast surface. */
|
|
16
17
|
|
|
17
|
-
.wt-region,
|
|
18
|
-
.wt-slot {
|
|
18
|
+
:where(.wt-root) .wt-region,
|
|
19
|
+
:where(.wt-root) .wt-slot {
|
|
19
20
|
display: contents;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/* ===== Shared button primitive (44px touch target) ===== */
|
|
23
|
-
.wt-btn {
|
|
24
|
+
:where(.wt-root) .wt-btn {
|
|
24
25
|
display: inline-flex;
|
|
25
26
|
align-items: center;
|
|
26
27
|
justify-content: center;
|
|
@@ -36,11 +37,11 @@
|
|
|
36
37
|
user-select: none;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
.wt-btn:active {
|
|
40
|
+
:where(.wt-root) .wt-btn:active {
|
|
40
41
|
background: var(--text-active);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
.wt-btn svg {
|
|
44
|
+
:where(.wt-root) .wt-btn svg {
|
|
44
45
|
width: var(--icon);
|
|
45
46
|
height: var(--icon);
|
|
46
47
|
stroke: currentcolor;
|
|
@@ -54,9 +55,10 @@
|
|
|
54
55
|
Top-centre on every form factor (both desktop and the tabbed mobile UI dock a
|
|
55
56
|
bar at the bottom, so a bottom toast would collide with it; top-centre is
|
|
56
57
|
clear and the same everywhere). The vv-top/safe-top offset keeps it below the
|
|
57
|
-
notch and follows iOS's visual-viewport shift.
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
notch and follows iOS's visual-viewport shift. Absolute against .wt-root,
|
|
59
|
+
like all chrome (identical geometry in wt-viewport mode; see 01-scope.css). */
|
|
60
|
+
:where(.wt-root) .wt-toast {
|
|
61
|
+
position: absolute;
|
|
60
62
|
top: calc(var(--safe-top, 0px) + var(--vv-top, 0px) + var(--sp-3));
|
|
61
63
|
left: 50%;
|
|
62
64
|
transform: translateX(-50%);
|
|
@@ -77,7 +79,7 @@
|
|
|
77
79
|
opacity: 0;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
.wt-toast.visible {
|
|
82
|
+
:where(.wt-root) .wt-toast.visible {
|
|
81
83
|
display: block;
|
|
82
84
|
opacity: 1;
|
|
83
85
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* ===== Connection banner (connectionBanner feature) =====
|
|
2
|
+
Top-centre on every form factor (same rationale as the toast: a bottom bar is
|
|
3
|
+
always docked now). Absolute against .wt-root, like all chrome (identical
|
|
4
|
+
geometry in wt-viewport mode; see 01-scope.css). */
|
|
5
|
+
:where(.wt-root) .wt-conn-banner {
|
|
6
|
+
position: absolute;
|
|
7
|
+
top: calc(var(--safe-top, 0px) + var(--vv-top, 0px) + var(--sp-3));
|
|
8
|
+
left: 50%;
|
|
9
|
+
transform: translateX(-50%);
|
|
10
|
+
max-width: calc(100% - 2 * var(--sp-3));
|
|
11
|
+
padding: var(--sp-1) var(--sp-3);
|
|
12
|
+
background: var(--surface);
|
|
13
|
+
backdrop-filter: var(--blur);
|
|
14
|
+
border: 1px solid var(--border);
|
|
15
|
+
border-radius: var(--radius);
|
|
16
|
+
color: var(--text);
|
|
17
|
+
font-family: var(--font-ui);
|
|
18
|
+
font-size: 0.8125rem;
|
|
19
|
+
font-weight: 500;
|
|
20
|
+
text-align: center;
|
|
21
|
+
z-index: 20;
|
|
22
|
+
pointer-events: none; /* passive status surface: never steal taps from the terminal beneath */
|
|
23
|
+
display: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:where(.wt-root) .wt-conn-banner.visible {
|
|
27
|
+
display: block;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
:where(.wt-root) .wt-conn-banner[data-state="reconnecting"] {
|
|
31
|
+
border-color: oklch(70% 0.15 60deg / 40%);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:where(.wt-root) .wt-conn-banner[data-state="offline"] {
|
|
35
|
+
color: #f55;
|
|
36
|
+
border-color: oklch(60% 0.2 25deg / 50%);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
:where(.wt-root) .wt-conn-banner[data-state="restarted"] {
|
|
40
|
+
color: #fa5;
|
|
41
|
+
border-color: oklch(70% 0.15 60deg / 40%);
|
|
42
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* ===== Scroll-to-bottom (scrollToBottom feature) =====
|
|
2
|
+
A compact floating icon button docked in the bottom-right corner, shown only
|
|
3
|
+
while the user has scrolled up. Same button on both form factors (the old
|
|
4
|
+
desktop bottom-centre text pill was dropped in the design-system switch);
|
|
5
|
+
each form factor just lifts it above its own bottom bar. Size + icon
|
|
6
|
+
centering come from the .wt-btn primitive (a 44px icon button); this rule
|
|
7
|
+
adds only the floating chrome and placement. Absolute against .wt-root, like
|
|
8
|
+
all chrome (identical geometry in wt-viewport mode; see 01-scope.css). */
|
|
9
|
+
:where(.wt-root) .wt-scroll-bottom {
|
|
10
|
+
position: absolute;
|
|
11
|
+
right: var(--sp-3);
|
|
12
|
+
bottom: calc(var(--safe-bottom, 0px) + var(--sp-3) + var(--kb-inset, 0px));
|
|
13
|
+
background: var(--surface);
|
|
14
|
+
backdrop-filter: var(--blur);
|
|
15
|
+
border: 1px solid var(--border);
|
|
16
|
+
/* Match the tab "+" buttons (and every other .wt-btn), which round to
|
|
17
|
+
--radius-inner. Both are 44px boxes, so matching the corner radius makes
|
|
18
|
+
the FAB read as the same-sized button as the new-tab "+" beside/above it
|
|
19
|
+
rather than a rounder, different-looking control. */
|
|
20
|
+
border-radius: var(--radius-inner);
|
|
21
|
+
/* Full --text icon, matching the switcher's keyboard/switch/"+" icons (which
|
|
22
|
+
use .wt-btn's default color). Was a muted 70% mix that read as grey next to
|
|
23
|
+
those white bar icons. */
|
|
24
|
+
color: var(--text);
|
|
25
|
+
z-index: 10;
|
|
26
|
+
display: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* The scroll slot gets .scrolled-up toggled by the feature; show the button. */
|
|
30
|
+
:where(.wt-root) .wt-slot-scroll.scrolled-up .wt-scroll-bottom {
|
|
31
|
+
display: inline-flex;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Hover brightens the border; press fills the background (the .wt-btn :active
|
|
35
|
+
fill would otherwise be masked by this button's own surface fill). The icon
|
|
36
|
+
is already full --text, so hover no longer needs to change it. */
|
|
37
|
+
:where(.wt-root) .wt-scroll-bottom:hover {
|
|
38
|
+
border-color: color-mix(in oklch, var(--text) 35%, transparent);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:where(.wt-root) .wt-scroll-bottom:active {
|
|
42
|
+
background: var(--text-active);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Mobile + tabbed: lift above the switcher bar and the keyboard (WS3), and pull
|
|
46
|
+
the right edge in to --sp-2 so the button sits directly above the switcher's
|
|
47
|
+
"+" (whose right edge is inset by the bar's --sp-2 padding). Both are 44px
|
|
48
|
+
border-box boxes, so matching the right inset lines them up in one vertical
|
|
49
|
+
column (they were 4px out: the FAB's base --sp-3 vs the "+"'s --sp-2). */
|
|
50
|
+
@media (pointer: coarse) {
|
|
51
|
+
:where(.wt-root.wt-tabbed) .wt-scroll-bottom {
|
|
52
|
+
right: var(--sp-2);
|
|
53
|
+
bottom: calc(
|
|
54
|
+
var(--wt-switcher-h) + var(--safe-bottom, 0px) + var(--kb-inset, 0px) + var(--sp-2)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Desktop + tabbed: the tab strip docks at the bottom, so lift the button above
|
|
60
|
+
it with a clear gap. */
|
|
61
|
+
@media (pointer: fine) {
|
|
62
|
+
:where(.wt-root.wt-tabbed) .wt-scroll-bottom {
|
|
63
|
+
bottom: calc(var(--wt-tabbar-h, 44px) + var(--sp-3));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* ===== Context menu (contextMenu feature) =====
|
|
2
|
+
Absolute against .wt-root, like all chrome; the shared point-anchored
|
|
3
|
+
positioner (src/features/menu-position.ts) converts its viewport-clamped
|
|
4
|
+
coordinates into root-relative offsets, so placement is identical in both
|
|
5
|
+
layout modes. */
|
|
6
|
+
:where(.wt-root) .wt-ctx-menu {
|
|
7
|
+
position: absolute;
|
|
8
|
+
display: none;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
background: var(--surface);
|
|
11
|
+
backdrop-filter: var(--blur);
|
|
12
|
+
border: 1px solid var(--border);
|
|
13
|
+
border-radius: var(--radius);
|
|
14
|
+
padding: var(--sp-1);
|
|
15
|
+
z-index: 30;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
:where(.wt-root) .wt-ctx-menu.visible {
|
|
19
|
+
display: flex;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:where(.wt-root) .wt-ctx-menu button {
|
|
23
|
+
background: transparent;
|
|
24
|
+
color: var(--text);
|
|
25
|
+
border: none;
|
|
26
|
+
border-radius: var(--radius-inner);
|
|
27
|
+
font-family: var(--font-ui);
|
|
28
|
+
font-size: 0.8125rem;
|
|
29
|
+
font-weight: 500;
|
|
30
|
+
padding: var(--sp-1) var(--sp-3);
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
text-align: left;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
:where(.wt-root) .wt-ctx-menu button:active {
|
|
36
|
+
background: var(--text-active);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@media (pointer: coarse) {
|
|
40
|
+
:where(.wt-root) .wt-ctx-menu button {
|
|
41
|
+
min-height: var(--touch-target);
|
|
42
|
+
padding-block: var(--sp-2);
|
|
43
|
+
touch-action: manipulation;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/* ===== Key toolbar (mobileToolbar feature; touch devices only) =====
|
|
2
|
+
Uses interpolate-size: allow-keywords (on .wt-root) to animate
|
|
3
|
+
width/height between a fixed collapsed size and auto (intrinsic).
|
|
4
|
+
Toggle is first in DOM so it's at top-left of the grid — visible
|
|
5
|
+
when the container clips to 1-cell size. Container is right-anchored
|
|
6
|
+
so it grows leftward on open. */
|
|
7
|
+
:where(.wt-root) .key-toolbar {
|
|
8
|
+
display: contents;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Touch-only toolbar children: the on-screen Esc/Tab/arrows/hamburger
|
|
12
|
+
are useless on desktop where the user has a real keyboard. Hidden by
|
|
13
|
+
default; the @media (pointer: coarse) block below restores them. */
|
|
14
|
+
:where(.wt-root) .kb-key,
|
|
15
|
+
:where(.wt-root) .kb-toggle {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@media (pointer: coarse) {
|
|
20
|
+
:where(.wt-root) .key-toolbar {
|
|
21
|
+
/* Absolute against .wt-root (identical geometry in wt-viewport mode; see
|
|
22
|
+
01-scope.css). --vv-top keeps it following iOS's visual-viewport shift. */
|
|
23
|
+
position: absolute;
|
|
24
|
+
top: calc(var(--safe-top) + var(--sp-2) + var(--vv-top, 0px));
|
|
25
|
+
right: var(--sp-2);
|
|
26
|
+
z-index: 10;
|
|
27
|
+
display: grid;
|
|
28
|
+
grid-template-columns: repeat(4, var(--touch-target)) var(--touch-target);
|
|
29
|
+
grid-template-rows: var(--touch-target) var(--touch-target);
|
|
30
|
+
place-content: start end;
|
|
31
|
+
gap: var(--sp-1);
|
|
32
|
+
padding: var(--sp-1);
|
|
33
|
+
background: var(--surface);
|
|
34
|
+
backdrop-filter: var(--blur);
|
|
35
|
+
border: 1px solid var(--border);
|
|
36
|
+
border-radius: var(--radius);
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
width: auto;
|
|
39
|
+
height: auto;
|
|
40
|
+
transition:
|
|
41
|
+
width var(--dur-enter) var(--ease-standard),
|
|
42
|
+
height var(--dur-enter) var(--ease-standard);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Collapsed: fixed size = 1 button + padding + border. Content is
|
|
46
|
+
right-aligned (justify-content:end) so the toggle (col 5) stays at
|
|
47
|
+
the right edge, visible within the clipped area. --pill-outer
|
|
48
|
+
includes the 2px border (box-sizing:border-box); omitting it left
|
|
49
|
+
the content box 2px short, pinning the button top-right with
|
|
50
|
+
unequal bottom/left gaps. */
|
|
51
|
+
:where(.wt-root) .key-toolbar.collapsed {
|
|
52
|
+
width: var(--pill-outer);
|
|
53
|
+
height: var(--pill-outer);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Grid placement: toggle at col 5 row 1 (top-right). */
|
|
57
|
+
:where(.wt-root) .kb-toggle {
|
|
58
|
+
grid-area: 1 / 5;
|
|
59
|
+
position: relative;
|
|
60
|
+
}
|
|
61
|
+
:where(.wt-root) .kb-r1c1 {
|
|
62
|
+
grid-area: 1 / 1;
|
|
63
|
+
}
|
|
64
|
+
:where(.wt-root) .kb-r1c2 {
|
|
65
|
+
grid-area: 1 / 2;
|
|
66
|
+
}
|
|
67
|
+
:where(.wt-root) .kb-r1c3 {
|
|
68
|
+
grid-area: 1 / 3;
|
|
69
|
+
}
|
|
70
|
+
:where(.wt-root) .kb-r1c4 {
|
|
71
|
+
grid-area: 1 / 4;
|
|
72
|
+
}
|
|
73
|
+
:where(.wt-root) .kb-r2c1 {
|
|
74
|
+
grid-area: 2 / 1;
|
|
75
|
+
}
|
|
76
|
+
:where(.wt-root) .kb-r2c2 {
|
|
77
|
+
grid-area: 2 / 2;
|
|
78
|
+
}
|
|
79
|
+
:where(.wt-root) .kb-r2c3 {
|
|
80
|
+
grid-area: 2 / 3;
|
|
81
|
+
}
|
|
82
|
+
:where(.wt-root) .kb-r2c4 {
|
|
83
|
+
grid-area: 2 / 4;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Keys fade in/out during open/close. The container's width/height
|
|
87
|
+
transition handles the background slide; opacity handles the
|
|
88
|
+
individual button appearance. Stagger the fade slightly after
|
|
89
|
+
the container starts growing (delay on open) and before it
|
|
90
|
+
finishes shrinking (no delay on close). */
|
|
91
|
+
:where(.wt-root) .kb-key {
|
|
92
|
+
opacity: 1;
|
|
93
|
+
transition: opacity var(--dur-standard) var(--ease-enter) 0.05s;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
:where(.wt-root) .key-toolbar.collapsed .kb-key {
|
|
97
|
+
opacity: 0;
|
|
98
|
+
transition: opacity var(--dur-exit) var(--ease-exit);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Shared button */
|
|
102
|
+
:where(.wt-root) .key-toolbar button {
|
|
103
|
+
width: var(--touch-target);
|
|
104
|
+
height: var(--touch-target);
|
|
105
|
+
background: transparent;
|
|
106
|
+
color: var(--text);
|
|
107
|
+
border: none;
|
|
108
|
+
border-radius: var(--radius-inner);
|
|
109
|
+
font-size: 0.9375rem;
|
|
110
|
+
font-family: var(--font-ui);
|
|
111
|
+
font-weight: 500;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
touch-action: manipulation;
|
|
114
|
+
user-select: none;
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
:where(.wt-root) .key-toolbar button:active {
|
|
121
|
+
background: var(--text-active);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Sticky Ctrl, armed: inverted fill so the toggle state reads clearly
|
|
125
|
+
in the monochrome palette (stays filled while pressed). */
|
|
126
|
+
:where(.wt-root) .kb-key.armed,
|
|
127
|
+
:where(.wt-root) .kb-key.armed:active {
|
|
128
|
+
background: var(--text);
|
|
129
|
+
color: var(--bg);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
:where(.wt-root) .key-toolbar button svg {
|
|
133
|
+
width: var(--icon);
|
|
134
|
+
height: var(--icon);
|
|
135
|
+
stroke: currentcolor;
|
|
136
|
+
fill: none;
|
|
137
|
+
stroke-width: 2;
|
|
138
|
+
stroke-linecap: round;
|
|
139
|
+
stroke-linejoin: round;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Toggle icon crossfade. Both icons are stacked absolute so they
|
|
143
|
+
can independently fade + rotate during the open<->close
|
|
144
|
+
transition. inset:0 + margin:auto centers them within the
|
|
145
|
+
button (works because the SVGs carry explicit width/height); a
|
|
146
|
+
bare `position: absolute` would pin them at the top-left, giving
|
|
147
|
+
the toggle visibly unequal left/right padding when collapsed. */
|
|
148
|
+
:where(.wt-root) .kb-toggle .icon-hamburger,
|
|
149
|
+
:where(.wt-root) .kb-toggle .icon-close {
|
|
150
|
+
position: absolute;
|
|
151
|
+
inset: 0;
|
|
152
|
+
margin: auto;
|
|
153
|
+
transition:
|
|
154
|
+
opacity var(--dur-standard) linear,
|
|
155
|
+
transform var(--dur-standard) linear;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
:where(.wt-root) .kb-toggle .icon-close {
|
|
159
|
+
opacity: 1;
|
|
160
|
+
transform: rotate(0deg);
|
|
161
|
+
}
|
|
162
|
+
:where(.wt-root) .kb-toggle .icon-hamburger {
|
|
163
|
+
opacity: 0;
|
|
164
|
+
transform: rotate(-90deg);
|
|
165
|
+
}
|
|
166
|
+
:where(.wt-root) .key-toolbar.collapsed .kb-toggle .icon-close {
|
|
167
|
+
opacity: 0;
|
|
168
|
+
transform: rotate(90deg);
|
|
169
|
+
}
|
|
170
|
+
:where(.wt-root) .key-toolbar.collapsed .kb-toggle .icon-hamburger {
|
|
171
|
+
opacity: 1;
|
|
172
|
+
transform: rotate(0deg);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* No-transition on first render */
|
|
176
|
+
:where(.wt-root) .key-toolbar.no-transition,
|
|
177
|
+
:where(.wt-root) .key-toolbar.no-transition .kb-toggle .icon-hamburger,
|
|
178
|
+
:where(.wt-root) .key-toolbar.no-transition .kb-toggle .icon-close {
|
|
179
|
+
transition: none;
|
|
180
|
+
}
|
|
181
|
+
}
|