@stacksjs/components 0.2.166 → 0.2.168
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/dist/{Sidebar-56px4jha.stx → Sidebar-v7ecx5gs.stx} +6 -4
- package/dist/{SidebarSpace-t68ywe45.stx → SidebarSpace-r062weq2.stx} +13 -2
- package/dist/SidebarSpaceSwitcher-az9wqm87.stx +189 -0
- package/dist/{SidebarSpaces-w17e6ma4.stx → SidebarSpaces-f149wvf1.stx} +57 -4
- package/dist/index.js +8 -8
- package/package.json +3 -3
- package/src/ui/sidebar/Sidebar.stx +6 -4
- package/src/ui/sidebar/SidebarSpace.stx +13 -2
- package/src/ui/sidebar/SidebarSpaceSwitcher.stx +150 -59
- package/src/ui/sidebar/SidebarSpaces.stx +57 -4
- package/dist/SidebarSpaceSwitcher-882njcxq.stx +0 -98
|
@@ -639,10 +639,12 @@ defineExpose({ collapsed, toggle: toggleCollapse, syncRouteSelection })
|
|
|
639
639
|
/* Width is listed here because this rule outranks the pane's
|
|
640
640
|
`transition-[width]` utility class and would otherwise drop it. */
|
|
641
641
|
transition:
|
|
642
|
-
--stx-space-from 420ms ease,
|
|
643
|
-
--stx-space-to 420ms ease,
|
|
644
|
-
--stx-space-ink 420ms ease,
|
|
645
|
-
--stx-space-accent 420ms ease,
|
|
642
|
+
--stx-space-from var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
643
|
+
--stx-space-to var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
644
|
+
--stx-space-ink var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
645
|
+
--stx-space-accent var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
646
|
+
/* Collapsing the pane is a different intent from changing space, so it
|
|
647
|
+
keeps its own timing. */
|
|
646
648
|
width 300ms ease-out;
|
|
647
649
|
}
|
|
648
650
|
|
|
@@ -45,7 +45,7 @@ export const actionClasses = [
|
|
|
45
45
|
'flex w-full items-center gap-[8px]',
|
|
46
46
|
'h-[30px] rounded-[8px] px-[6px]',
|
|
47
47
|
'text-[13px] leading-[16px]',
|
|
48
|
-
'opacity-55
|
|
48
|
+
'opacity-55 stx-space-timed',
|
|
49
49
|
'hover:bg-white/45 hover:opacity-100 dark:hover:bg-white/8',
|
|
50
50
|
'cursor-default',
|
|
51
51
|
].join(' ')
|
|
@@ -53,7 +53,7 @@ export const actionClasses = [
|
|
|
53
53
|
export const clearClasses = [
|
|
54
54
|
'flex items-center gap-[4px] rounded-[6px] px-[5px] py-[2px]',
|
|
55
55
|
'text-[11px] leading-[14px]',
|
|
56
|
-
'opacity-45
|
|
56
|
+
'opacity-45 stx-space-timed',
|
|
57
57
|
'hover:opacity-90',
|
|
58
58
|
'cursor-default',
|
|
59
59
|
].join(' ')
|
|
@@ -150,6 +150,17 @@ function onAction(event) {
|
|
|
150
150
|
</section>
|
|
151
151
|
|
|
152
152
|
<style scoped>
|
|
153
|
+
/* Anything that changes with the space shares one duration and one curve, so a
|
|
154
|
+
swipe is a single motion. See `--stx-space-settle` in SidebarSpaces. */
|
|
155
|
+
.stx-space-timed {
|
|
156
|
+
transition:
|
|
157
|
+
background-color var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
158
|
+
opacity var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
159
|
+
color var(--stx-space-settle, 420ms) var(--stx-space-ease, ease);
|
|
160
|
+
}
|
|
161
|
+
/* Hover is a separate intent and stays quick. */
|
|
162
|
+
.stx-space-timed:hover { transition-duration: 120ms; }
|
|
163
|
+
|
|
153
164
|
/* The title icon rides the space accent. Published as a custom property by
|
|
154
165
|
SidebarSpaces so it crossfades along with the panel instead of cutting. */
|
|
155
166
|
.stx-space-title-icon {
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
// The indicator along the bottom of an Arc-style sidebar: one dot per space,
|
|
3
|
+
// plus an optional trailing "+".
|
|
4
|
+
//
|
|
5
|
+
// Dots rather than one icon per space, which is what Dia does and what this
|
|
6
|
+
// used to get wrong. An icon rail costs ~28px of width each, so it fits about
|
|
7
|
+
// eight spaces in a 280px sidebar and then overflows — and with every space
|
|
8
|
+
// showing the same default folder glyph, thirteen of them read as a broken
|
|
9
|
+
// progress bar rather than as a switcher. A dot is 6px, says only "which of
|
|
10
|
+
// how many", and leaves the identifying work to the space title and the tint,
|
|
11
|
+
// which is where it belongs.
|
|
12
|
+
//
|
|
13
|
+
// The buttons are markup only. `<SidebarSpaces>` owns the single click handler
|
|
14
|
+
// and flips `data-space-current`, so the rail never holds its own copy of
|
|
15
|
+
// which space is active — there is exactly one source of truth.
|
|
16
|
+
export const spaces = ($props.spaces || []).map(space => ({
|
|
17
|
+
id: space.id,
|
|
18
|
+
label: space.label || space.id,
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
export const active = $props.active || (spaces[0] && spaces[0].id) || ''
|
|
22
|
+
export const showAdd = $props.showAdd ?? false
|
|
23
|
+
export const addLabel = $props.addLabel || 'New space'
|
|
24
|
+
|
|
25
|
+
// A single space has nothing to indicate. Dia hides the row entirely rather
|
|
26
|
+
// than showing one lonely dot, and so does this — unless "+" needs a home.
|
|
27
|
+
export const showDots = spaces.length > 1
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<div
|
|
31
|
+
data-space-switcher
|
|
32
|
+
class="stx-space-rail"
|
|
33
|
+
role="tablist"
|
|
34
|
+
aria-label="Spaces"
|
|
35
|
+
aria-orientation="horizontal"
|
|
36
|
+
>
|
|
37
|
+
<!--
|
|
38
|
+
A spacer of the same width as the trailing button, so the dots are centred
|
|
39
|
+
on the sidebar rather than on the space left over beside "+".
|
|
40
|
+
-->
|
|
41
|
+
<span class="stx-space-rail-gutter" aria-hidden="true"></span>
|
|
42
|
+
|
|
43
|
+
<div class="stx-space-dots">
|
|
44
|
+
@if(showDots)
|
|
45
|
+
@foreach(spaces as space)
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
data-space-target="{{ space.id }}"
|
|
49
|
+
data-space-current="{{ space.id === active ? 'true' : 'false' }}"
|
|
50
|
+
class="stx-space-dot"
|
|
51
|
+
role="tab"
|
|
52
|
+
aria-selected="{{ space.id === active ? 'true' : 'false' }}"
|
|
53
|
+
aria-label="{{ space.label }}"
|
|
54
|
+
title="{{ space.label }}"
|
|
55
|
+
tabindex="{{ space.id === active ? '0' : '-1' }}"
|
|
56
|
+
>
|
|
57
|
+
<!--
|
|
58
|
+
The dot is a child rather than the button itself: the button stays a
|
|
59
|
+
comfortable target while the mark stays small.
|
|
60
|
+
-->
|
|
61
|
+
<span class="stx-space-dot-mark" aria-hidden="true"></span>
|
|
62
|
+
</button>
|
|
63
|
+
@endforeach
|
|
64
|
+
@endif
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<span class="stx-space-rail-gutter">
|
|
68
|
+
@if(showAdd)
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
data-space-add
|
|
72
|
+
class="stx-space-add"
|
|
73
|
+
aria-label="{{ addLabel }}"
|
|
74
|
+
title="{{ addLabel }}"
|
|
75
|
+
>
|
|
76
|
+
<span class="i-f7-plus h-[11px] w-[11px]" aria-hidden="true"></span>
|
|
77
|
+
</button>
|
|
78
|
+
@endif
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<style scoped>
|
|
83
|
+
.stx-space-rail {
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
height: 34px;
|
|
88
|
+
padding: 0 8px 6px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.stx-space-rail-gutter {
|
|
92
|
+
display: grid;
|
|
93
|
+
place-items: center;
|
|
94
|
+
width: 22px;
|
|
95
|
+
flex-shrink: 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.stx-space-dots {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex: 1;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
gap: 2px;
|
|
104
|
+
min-width: 0;
|
|
105
|
+
/* Many spaces scroll rather than squeeze: dots that shrink to fit stop
|
|
106
|
+
reading as dots, and a sidebar cannot get wider. The bar is hidden because
|
|
107
|
+
the dots are the affordance. */
|
|
108
|
+
overflow-x: auto;
|
|
109
|
+
scrollbar-width: none;
|
|
110
|
+
}
|
|
111
|
+
.stx-space-dots::-webkit-scrollbar { display: none; }
|
|
112
|
+
|
|
113
|
+
.stx-space-dot {
|
|
114
|
+
display: grid;
|
|
115
|
+
place-items: center;
|
|
116
|
+
width: 14px;
|
|
117
|
+
height: 18px;
|
|
118
|
+
flex-shrink: 0;
|
|
119
|
+
background: transparent;
|
|
120
|
+
border: 0;
|
|
121
|
+
padding: 0;
|
|
122
|
+
cursor: default;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.stx-space-dot-mark {
|
|
126
|
+
width: 6px;
|
|
127
|
+
height: 6px;
|
|
128
|
+
border-radius: 9999px;
|
|
129
|
+
background: currentColor;
|
|
130
|
+
opacity: 0.25;
|
|
131
|
+
/* One duration and one curve for everything that moves when the space
|
|
132
|
+
changes — see `--stx-space-settle` in SidebarSpaces. Independent timings
|
|
133
|
+
are what made a swipe read as several things happening at once. */
|
|
134
|
+
transition:
|
|
135
|
+
opacity var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
136
|
+
background-color var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
137
|
+
transform var(--stx-space-settle, 420ms) var(--stx-space-ease, ease);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Hover is not part of a space change, so it stays quick. */
|
|
141
|
+
.stx-space-dot:hover .stx-space-dot-mark {
|
|
142
|
+
opacity: 0.5;
|
|
143
|
+
transition-duration: 120ms;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.stx-space-dot[data-space-current="true"] .stx-space-dot-mark {
|
|
147
|
+
opacity: 1;
|
|
148
|
+
background: var(--stx-space-accent, currentColor);
|
|
149
|
+
/* Barely larger. Enough to find at a glance, not enough to jump. */
|
|
150
|
+
transform: scale(1.15);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.stx-space-dot:focus-visible {
|
|
154
|
+
outline: 2px solid var(--stx-space-accent, currentColor);
|
|
155
|
+
outline-offset: 2px;
|
|
156
|
+
border-radius: 9999px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.stx-space-add {
|
|
160
|
+
display: grid;
|
|
161
|
+
place-items: center;
|
|
162
|
+
width: 20px;
|
|
163
|
+
height: 20px;
|
|
164
|
+
border-radius: 6px;
|
|
165
|
+
color: currentColor;
|
|
166
|
+
opacity: 0.35;
|
|
167
|
+
background: transparent;
|
|
168
|
+
border: 0;
|
|
169
|
+
padding: 0;
|
|
170
|
+
cursor: default;
|
|
171
|
+
transition: opacity 120ms ease, background-color 120ms ease;
|
|
172
|
+
}
|
|
173
|
+
.stx-space-add:hover {
|
|
174
|
+
opacity: 0.8;
|
|
175
|
+
background: rgba(255, 255, 255, 0.4);
|
|
176
|
+
}
|
|
177
|
+
.stx-space-add:focus-visible {
|
|
178
|
+
outline: 2px solid var(--stx-space-accent, currentColor);
|
|
179
|
+
outline-offset: 1px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@media (prefers-color-scheme: dark) {
|
|
183
|
+
.stx-space-add:hover { background: rgba(255, 255, 255, 0.1); }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@media (prefers-reduced-motion: reduce) {
|
|
187
|
+
.stx-space-dot-mark { transition: none; }
|
|
188
|
+
}
|
|
189
|
+
</style>
|
|
@@ -106,6 +106,20 @@ function tintTarget() {
|
|
|
106
106
|
return root.closest('[data-stx-sidebar]') || root
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Publish the settle timing so CSS and the track animation cannot drift.
|
|
111
|
+
*
|
|
112
|
+
* `SETTLE_MS` drives the track's transform from JS; everything else that moves
|
|
113
|
+
* during a space change reads these properties. Written once at mount rather
|
|
114
|
+
* than duplicated as literals in four stylesheets, which is how they came to
|
|
115
|
+
* disagree — 420ms on one curve, 420ms on another, 300ms, and 150ms.
|
|
116
|
+
*/
|
|
117
|
+
function publishTiming() {
|
|
118
|
+
const style = tintTarget().style
|
|
119
|
+
style.setProperty('--stx-space-settle', `${SETTLE_MS}ms`)
|
|
120
|
+
style.setProperty('--stx-space-ease', SETTLE_EASE)
|
|
121
|
+
}
|
|
122
|
+
|
|
109
123
|
function applyTint(position) {
|
|
110
124
|
const tint = tints[position]
|
|
111
125
|
if (!tint) return
|
|
@@ -120,6 +134,32 @@ function applyTint(position) {
|
|
|
120
134
|
style.setProperty('--stx-space-dark-accent', tint.dark.accent)
|
|
121
135
|
}
|
|
122
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Keep the active dot on screen.
|
|
139
|
+
*
|
|
140
|
+
* The dot row scrolls once there are more spaces than fit, and without this the
|
|
141
|
+
* active dot can sit outside the visible range — an indicator that indicates
|
|
142
|
+
* nothing. Measured on a sidebar with 22 spaces: the active one was the 20th,
|
|
143
|
+
* and none of the twelve you could see was lit.
|
|
144
|
+
*
|
|
145
|
+
* `block: 'nearest'` because this must never scroll the page vertically to
|
|
146
|
+
* bring a 6px dot into view.
|
|
147
|
+
*/
|
|
148
|
+
function revealCurrentDot(tab) {
|
|
149
|
+
const row = tab.closest('[data-space-switcher] .stx-space-dots')
|
|
150
|
+
if (!row || row.scrollWidth <= row.clientWidth) return
|
|
151
|
+
|
|
152
|
+
const rowBox = row.getBoundingClientRect()
|
|
153
|
+
const dotBox = tab.getBoundingClientRect()
|
|
154
|
+
if (dotBox.left >= rowBox.left && dotBox.right <= rowBox.right) return
|
|
155
|
+
|
|
156
|
+
tab.scrollIntoView({
|
|
157
|
+
inline: 'center',
|
|
158
|
+
block: 'nearest',
|
|
159
|
+
behavior: animates() ? 'smooth' : 'auto',
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
123
163
|
function markCurrent(position) {
|
|
124
164
|
const id = ids[position]
|
|
125
165
|
|
|
@@ -128,6 +168,7 @@ function markCurrent(position) {
|
|
|
128
168
|
tab.setAttribute('data-space-current', String(current))
|
|
129
169
|
tab.setAttribute('aria-selected', String(current))
|
|
130
170
|
tab.tabIndex = current ? 0 : -1
|
|
171
|
+
if (current) revealCurrentDot(tab)
|
|
131
172
|
})
|
|
132
173
|
|
|
133
174
|
root.querySelectorAll('[data-space]').forEach((panel) => {
|
|
@@ -452,6 +493,10 @@ onMount(() => {
|
|
|
452
493
|
tints = spaces.map(space => space.tint)
|
|
453
494
|
if (ids.length === 0) return
|
|
454
495
|
|
|
496
|
+
// Before the first paint, so nothing animates on the default timing once and
|
|
497
|
+
// the shared one afterwards.
|
|
498
|
+
publishTiming()
|
|
499
|
+
|
|
455
500
|
const startIndex = clampIndex(config.index || 0, ids.length)
|
|
456
501
|
const saved = restored()
|
|
457
502
|
const initial = saved === -1 ? startIndex : saved
|
|
@@ -560,11 +605,19 @@ defineExpose({
|
|
|
560
605
|
linear-gradient(180deg, rgba(255, 255, 255, 0.34) 0%, rgba(255, 255, 255, 0) 32%),
|
|
561
606
|
linear-gradient(180deg, var(--stx-space-from) 0%, var(--stx-space-to) 100%);
|
|
562
607
|
color: var(--stx-space-ink);
|
|
608
|
+
/* One duration and one curve for everything a space change moves.
|
|
609
|
+
This used to be four timings and three easings across the track, the
|
|
610
|
+
palette, the switcher and the space's own buttons — 420ms on one curve,
|
|
611
|
+
420ms on another, 300ms, 150ms — so a swipe read as several things
|
|
612
|
+
happening near each other rather than as one motion. Published as custom
|
|
613
|
+
properties so the pieces cannot drift apart again. */
|
|
614
|
+
--stx-space-settle: 420ms;
|
|
615
|
+
--stx-space-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
|
|
563
616
|
transition:
|
|
564
|
-
--stx-space-from
|
|
565
|
-
--stx-space-to
|
|
566
|
-
--stx-space-ink
|
|
567
|
-
--stx-space-accent
|
|
617
|
+
--stx-space-from var(--stx-space-settle) var(--stx-space-ease),
|
|
618
|
+
--stx-space-to var(--stx-space-settle) var(--stx-space-ease),
|
|
619
|
+
--stx-space-ink var(--stx-space-settle) var(--stx-space-ease),
|
|
620
|
+
--stx-space-accent var(--stx-space-settle) var(--stx-space-ease);
|
|
568
621
|
}
|
|
569
622
|
|
|
570
623
|
@media (prefers-color-scheme: dark) {
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
2
|
+
// ../../../../../../../tmp/stx-components-build-tmXNAI/stubs/stub-0.js
|
|
3
3
|
var stub_0_default = { __stx: true, src: "./components/CodeBlock.stx" };
|
|
4
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
4
|
+
// ../../../../../../../tmp/stx-components-build-tmXNAI/stubs/stub-1.js
|
|
5
5
|
var stub_1_default = { __stx: true, src: "./components/Footer.stx" };
|
|
6
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
6
|
+
// ../../../../../../../tmp/stx-components-build-tmXNAI/stubs/stub-2.js
|
|
7
7
|
var stub_2_default = { __stx: true, src: "./components/Hero.stx" };
|
|
8
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
8
|
+
// ../../../../../../../tmp/stx-components-build-tmXNAI/stubs/stub-3.js
|
|
9
9
|
var stub_3_default = { __stx: true, src: "./components/Installation.stx" };
|
|
10
10
|
// src/composables/useCopyCode.ts
|
|
11
11
|
function useCopyCode(options) {
|
|
@@ -1132,7 +1132,7 @@ var RadioGroupOption_default = "./RadioGroupOption-s1d6r221.stx";
|
|
|
1132
1132
|
// src/ui/select/Select.stx
|
|
1133
1133
|
var Select_default = "./Select-1vnd0brz.stx";
|
|
1134
1134
|
// src/ui/sidebar/Sidebar.stx
|
|
1135
|
-
var Sidebar_default = "./Sidebar-
|
|
1135
|
+
var Sidebar_default = "./Sidebar-v7ecx5gs.stx";
|
|
1136
1136
|
// src/ui/sidebar/SidebarFooter.stx
|
|
1137
1137
|
var SidebarFooter_default = "./SidebarFooter-s7skj92y.stx";
|
|
1138
1138
|
// src/ui/sidebar/SidebarHeader.stx
|
|
@@ -1144,11 +1144,11 @@ var SidebarPinned_default = "./SidebarPinned-tvj28bm0.stx";
|
|
|
1144
1144
|
// src/ui/sidebar/SidebarSection.stx
|
|
1145
1145
|
var SidebarSection_default = "./SidebarSection-zd9z35ch.stx";
|
|
1146
1146
|
// src/ui/sidebar/SidebarSpace.stx
|
|
1147
|
-
var SidebarSpace_default = "./SidebarSpace-
|
|
1147
|
+
var SidebarSpace_default = "./SidebarSpace-r062weq2.stx";
|
|
1148
1148
|
// src/ui/sidebar/SidebarSpaces.stx
|
|
1149
|
-
var SidebarSpaces_default = "./SidebarSpaces-
|
|
1149
|
+
var SidebarSpaces_default = "./SidebarSpaces-f149wvf1.stx";
|
|
1150
1150
|
// src/ui/sidebar/SidebarSpaceSwitcher.stx
|
|
1151
|
-
var SidebarSpaceSwitcher_default = "./SidebarSpaceSwitcher-
|
|
1151
|
+
var SidebarSpaceSwitcher_default = "./SidebarSpaceSwitcher-az9wqm87.stx";
|
|
1152
1152
|
// src/ui/sidebar/themes.ts
|
|
1153
1153
|
var macosColors = {
|
|
1154
1154
|
blue: "#0088ff",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/components",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.168",
|
|
5
5
|
"description": "A modern component library built with STX, featuring headwind utility classes and TypeScript syntax highlighting.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@iconify-json/f7": "^1.2.2",
|
|
66
66
|
"@cwcss/crosswind": "^0.2.14",
|
|
67
|
-
"@stacksjs/stx": "0.2.
|
|
68
|
-
"bun-plugin-stx": "0.2.
|
|
67
|
+
"@stacksjs/stx": "0.2.168",
|
|
68
|
+
"bun-plugin-stx": "0.2.168",
|
|
69
69
|
"ts-syntax-highlighter": "^0.2.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
@@ -639,10 +639,12 @@ defineExpose({ collapsed, toggle: toggleCollapse, syncRouteSelection })
|
|
|
639
639
|
/* Width is listed here because this rule outranks the pane's
|
|
640
640
|
`transition-[width]` utility class and would otherwise drop it. */
|
|
641
641
|
transition:
|
|
642
|
-
--stx-space-from 420ms ease,
|
|
643
|
-
--stx-space-to 420ms ease,
|
|
644
|
-
--stx-space-ink 420ms ease,
|
|
645
|
-
--stx-space-accent 420ms ease,
|
|
642
|
+
--stx-space-from var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
643
|
+
--stx-space-to var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
644
|
+
--stx-space-ink var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
645
|
+
--stx-space-accent var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
646
|
+
/* Collapsing the pane is a different intent from changing space, so it
|
|
647
|
+
keeps its own timing. */
|
|
646
648
|
width 300ms ease-out;
|
|
647
649
|
}
|
|
648
650
|
|
|
@@ -45,7 +45,7 @@ export const actionClasses = [
|
|
|
45
45
|
'flex w-full items-center gap-[8px]',
|
|
46
46
|
'h-[30px] rounded-[8px] px-[6px]',
|
|
47
47
|
'text-[13px] leading-[16px]',
|
|
48
|
-
'opacity-55
|
|
48
|
+
'opacity-55 stx-space-timed',
|
|
49
49
|
'hover:bg-white/45 hover:opacity-100 dark:hover:bg-white/8',
|
|
50
50
|
'cursor-default',
|
|
51
51
|
].join(' ')
|
|
@@ -53,7 +53,7 @@ export const actionClasses = [
|
|
|
53
53
|
export const clearClasses = [
|
|
54
54
|
'flex items-center gap-[4px] rounded-[6px] px-[5px] py-[2px]',
|
|
55
55
|
'text-[11px] leading-[14px]',
|
|
56
|
-
'opacity-45
|
|
56
|
+
'opacity-45 stx-space-timed',
|
|
57
57
|
'hover:opacity-90',
|
|
58
58
|
'cursor-default',
|
|
59
59
|
].join(' ')
|
|
@@ -150,6 +150,17 @@ function onAction(event) {
|
|
|
150
150
|
</section>
|
|
151
151
|
|
|
152
152
|
<style scoped>
|
|
153
|
+
/* Anything that changes with the space shares one duration and one curve, so a
|
|
154
|
+
swipe is a single motion. See `--stx-space-settle` in SidebarSpaces. */
|
|
155
|
+
.stx-space-timed {
|
|
156
|
+
transition:
|
|
157
|
+
background-color var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
158
|
+
opacity var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
159
|
+
color var(--stx-space-settle, 420ms) var(--stx-space-ease, ease);
|
|
160
|
+
}
|
|
161
|
+
/* Hover is a separate intent and stays quick. */
|
|
162
|
+
.stx-space-timed:hover { transition-duration: 120ms; }
|
|
163
|
+
|
|
153
164
|
/* The title icon rides the space accent. Published as a custom property by
|
|
154
165
|
SidebarSpaces so it crossfades along with the panel instead of cutting. */
|
|
155
166
|
.stx-space-title-icon {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
<script server>
|
|
2
|
-
// The
|
|
3
|
-
// an optional trailing "+".
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
// The indicator along the bottom of an Arc-style sidebar: one dot per space,
|
|
3
|
+
// plus an optional trailing "+".
|
|
4
|
+
//
|
|
5
|
+
// Dots rather than one icon per space, which is what Dia does and what this
|
|
6
|
+
// used to get wrong. An icon rail costs ~28px of width each, so it fits about
|
|
7
|
+
// eight spaces in a 280px sidebar and then overflows — and with every space
|
|
8
|
+
// showing the same default folder glyph, thirteen of them read as a broken
|
|
9
|
+
// progress bar rather than as a switcher. A dot is 6px, says only "which of
|
|
10
|
+
// how many", and leaves the identifying work to the space title and the tint,
|
|
11
|
+
// which is where it belongs.
|
|
6
12
|
//
|
|
7
13
|
// The buttons are markup only. `<SidebarSpaces>` owns the single click handler
|
|
8
14
|
// and flips `data-space-current`, so the rail never holds its own copy of
|
|
@@ -10,89 +16,174 @@
|
|
|
10
16
|
export const spaces = ($props.spaces || []).map(space => ({
|
|
11
17
|
id: space.id,
|
|
12
18
|
label: space.label || space.id,
|
|
13
|
-
icon: space.icon || 'i-f7-circle',
|
|
14
19
|
}))
|
|
15
20
|
|
|
16
21
|
export const active = $props.active || (spaces[0] && spaces[0].id) || ''
|
|
17
22
|
export const showAdd = $props.showAdd ?? false
|
|
18
23
|
export const addLabel = $props.addLabel || 'New space'
|
|
24
|
+
|
|
25
|
+
// A single space has nothing to indicate. Dia hides the row entirely rather
|
|
26
|
+
// than showing one lonely dot, and so does this — unless "+" needs a home.
|
|
27
|
+
export const showDots = spaces.length > 1
|
|
19
28
|
</script>
|
|
20
29
|
|
|
21
30
|
<div
|
|
22
31
|
data-space-switcher
|
|
23
|
-
class="
|
|
32
|
+
class="stx-space-rail"
|
|
24
33
|
role="tablist"
|
|
25
34
|
aria-label="Spaces"
|
|
26
35
|
aria-orientation="horizontal"
|
|
27
36
|
>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
<!--
|
|
38
|
+
A spacer of the same width as the trailing button, so the dots are centred
|
|
39
|
+
on the sidebar rather than on the space left over beside "+".
|
|
40
|
+
-->
|
|
41
|
+
<span class="stx-space-rail-gutter" aria-hidden="true"></span>
|
|
42
|
+
|
|
43
|
+
<div class="stx-space-dots">
|
|
44
|
+
@if(showDots)
|
|
45
|
+
@foreach(spaces as space)
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
data-space-target="{{ space.id }}"
|
|
49
|
+
data-space-current="{{ space.id === active ? 'true' : 'false' }}"
|
|
50
|
+
class="stx-space-dot"
|
|
51
|
+
role="tab"
|
|
52
|
+
aria-selected="{{ space.id === active ? 'true' : 'false' }}"
|
|
53
|
+
aria-label="{{ space.label }}"
|
|
54
|
+
title="{{ space.label }}"
|
|
55
|
+
tabindex="{{ space.id === active ? '0' : '-1' }}"
|
|
56
|
+
>
|
|
57
|
+
<!--
|
|
58
|
+
The dot is a child rather than the button itself: the button stays a
|
|
59
|
+
comfortable target while the mark stays small.
|
|
60
|
+
-->
|
|
61
|
+
<span class="stx-space-dot-mark" aria-hidden="true"></span>
|
|
62
|
+
</button>
|
|
63
|
+
@endforeach
|
|
64
|
+
@endif
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<span class="stx-space-rail-gutter">
|
|
68
|
+
@if(showAdd)
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
data-space-add
|
|
72
|
+
class="stx-space-add"
|
|
73
|
+
aria-label="{{ addLabel }}"
|
|
74
|
+
title="{{ addLabel }}"
|
|
75
|
+
>
|
|
76
|
+
<span class="i-f7-plus h-[11px] w-[11px]" aria-hidden="true"></span>
|
|
77
|
+
</button>
|
|
78
|
+
@endif
|
|
79
|
+
</span>
|
|
57
80
|
</div>
|
|
58
81
|
|
|
59
82
|
<style scoped>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
.stx-space-rail {
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
height: 34px;
|
|
88
|
+
padding: 0 8px 6px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.stx-space-rail-gutter {
|
|
65
92
|
display: grid;
|
|
66
93
|
place-items: center;
|
|
67
|
-
width:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
94
|
+
width: 22px;
|
|
95
|
+
flex-shrink: 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.stx-space-dots {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex: 1;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
gap: 2px;
|
|
104
|
+
min-width: 0;
|
|
105
|
+
/* Many spaces scroll rather than squeeze: dots that shrink to fit stop
|
|
106
|
+
reading as dots, and a sidebar cannot get wider. The bar is hidden because
|
|
107
|
+
the dots are the affordance. */
|
|
108
|
+
overflow-x: auto;
|
|
109
|
+
scrollbar-width: none;
|
|
110
|
+
}
|
|
111
|
+
.stx-space-dots::-webkit-scrollbar { display: none; }
|
|
112
|
+
|
|
113
|
+
.stx-space-dot {
|
|
114
|
+
display: grid;
|
|
115
|
+
place-items: center;
|
|
116
|
+
width: 14px;
|
|
117
|
+
height: 18px;
|
|
118
|
+
flex-shrink: 0;
|
|
72
119
|
background: transparent;
|
|
73
|
-
|
|
120
|
+
border: 0;
|
|
121
|
+
padding: 0;
|
|
74
122
|
cursor: default;
|
|
75
123
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
124
|
+
|
|
125
|
+
.stx-space-dot-mark {
|
|
126
|
+
width: 6px;
|
|
127
|
+
height: 6px;
|
|
128
|
+
border-radius: 9999px;
|
|
129
|
+
background: currentColor;
|
|
130
|
+
opacity: 0.25;
|
|
131
|
+
/* One duration and one curve for everything that moves when the space
|
|
132
|
+
changes — see `--stx-space-settle` in SidebarSpaces. Independent timings
|
|
133
|
+
are what made a swipe read as several things happening at once. */
|
|
134
|
+
transition:
|
|
135
|
+
opacity var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
136
|
+
background-color var(--stx-space-settle, 420ms) var(--stx-space-ease, ease),
|
|
137
|
+
transform var(--stx-space-settle, 420ms) var(--stx-space-ease, ease);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Hover is not part of a space change, so it stays quick. */
|
|
141
|
+
.stx-space-dot:hover .stx-space-dot-mark {
|
|
142
|
+
opacity: 0.5;
|
|
143
|
+
transition-duration: 120ms;
|
|
79
144
|
}
|
|
80
|
-
|
|
145
|
+
|
|
146
|
+
.stx-space-dot[data-space-current="true"] .stx-space-dot-mark {
|
|
81
147
|
opacity: 1;
|
|
82
|
-
|
|
83
|
-
|
|
148
|
+
background: var(--stx-space-accent, currentColor);
|
|
149
|
+
/* Barely larger. Enough to find at a glance, not enough to jump. */
|
|
150
|
+
transform: scale(1.15);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.stx-space-dot:focus-visible {
|
|
154
|
+
outline: 2px solid var(--stx-space-accent, currentColor);
|
|
155
|
+
outline-offset: 2px;
|
|
156
|
+
border-radius: 9999px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.stx-space-add {
|
|
160
|
+
display: grid;
|
|
161
|
+
place-items: center;
|
|
162
|
+
width: 20px;
|
|
163
|
+
height: 20px;
|
|
164
|
+
border-radius: 6px;
|
|
165
|
+
color: currentColor;
|
|
166
|
+
opacity: 0.35;
|
|
167
|
+
background: transparent;
|
|
168
|
+
border: 0;
|
|
169
|
+
padding: 0;
|
|
170
|
+
cursor: default;
|
|
171
|
+
transition: opacity 120ms ease, background-color 120ms ease;
|
|
84
172
|
}
|
|
85
|
-
.stx-space-
|
|
173
|
+
.stx-space-add:hover {
|
|
174
|
+
opacity: 0.8;
|
|
175
|
+
background: rgba(255, 255, 255, 0.4);
|
|
176
|
+
}
|
|
177
|
+
.stx-space-add:focus-visible {
|
|
86
178
|
outline: 2px solid var(--stx-space-accent, currentColor);
|
|
87
179
|
outline-offset: 1px;
|
|
88
180
|
}
|
|
89
181
|
|
|
90
182
|
@media (prefers-color-scheme: dark) {
|
|
91
|
-
.stx-space-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
183
|
+
.stx-space-add:hover { background: rgba(255, 255, 255, 0.1); }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@media (prefers-reduced-motion: reduce) {
|
|
187
|
+
.stx-space-dot-mark { transition: none; }
|
|
97
188
|
}
|
|
98
189
|
</style>
|
|
@@ -106,6 +106,20 @@ function tintTarget() {
|
|
|
106
106
|
return root.closest('[data-stx-sidebar]') || root
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Publish the settle timing so CSS and the track animation cannot drift.
|
|
111
|
+
*
|
|
112
|
+
* `SETTLE_MS` drives the track's transform from JS; everything else that moves
|
|
113
|
+
* during a space change reads these properties. Written once at mount rather
|
|
114
|
+
* than duplicated as literals in four stylesheets, which is how they came to
|
|
115
|
+
* disagree — 420ms on one curve, 420ms on another, 300ms, and 150ms.
|
|
116
|
+
*/
|
|
117
|
+
function publishTiming() {
|
|
118
|
+
const style = tintTarget().style
|
|
119
|
+
style.setProperty('--stx-space-settle', `${SETTLE_MS}ms`)
|
|
120
|
+
style.setProperty('--stx-space-ease', SETTLE_EASE)
|
|
121
|
+
}
|
|
122
|
+
|
|
109
123
|
function applyTint(position) {
|
|
110
124
|
const tint = tints[position]
|
|
111
125
|
if (!tint) return
|
|
@@ -120,6 +134,32 @@ function applyTint(position) {
|
|
|
120
134
|
style.setProperty('--stx-space-dark-accent', tint.dark.accent)
|
|
121
135
|
}
|
|
122
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Keep the active dot on screen.
|
|
139
|
+
*
|
|
140
|
+
* The dot row scrolls once there are more spaces than fit, and without this the
|
|
141
|
+
* active dot can sit outside the visible range — an indicator that indicates
|
|
142
|
+
* nothing. Measured on a sidebar with 22 spaces: the active one was the 20th,
|
|
143
|
+
* and none of the twelve you could see was lit.
|
|
144
|
+
*
|
|
145
|
+
* `block: 'nearest'` because this must never scroll the page vertically to
|
|
146
|
+
* bring a 6px dot into view.
|
|
147
|
+
*/
|
|
148
|
+
function revealCurrentDot(tab) {
|
|
149
|
+
const row = tab.closest('[data-space-switcher] .stx-space-dots')
|
|
150
|
+
if (!row || row.scrollWidth <= row.clientWidth) return
|
|
151
|
+
|
|
152
|
+
const rowBox = row.getBoundingClientRect()
|
|
153
|
+
const dotBox = tab.getBoundingClientRect()
|
|
154
|
+
if (dotBox.left >= rowBox.left && dotBox.right <= rowBox.right) return
|
|
155
|
+
|
|
156
|
+
tab.scrollIntoView({
|
|
157
|
+
inline: 'center',
|
|
158
|
+
block: 'nearest',
|
|
159
|
+
behavior: animates() ? 'smooth' : 'auto',
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
123
163
|
function markCurrent(position) {
|
|
124
164
|
const id = ids[position]
|
|
125
165
|
|
|
@@ -128,6 +168,7 @@ function markCurrent(position) {
|
|
|
128
168
|
tab.setAttribute('data-space-current', String(current))
|
|
129
169
|
tab.setAttribute('aria-selected', String(current))
|
|
130
170
|
tab.tabIndex = current ? 0 : -1
|
|
171
|
+
if (current) revealCurrentDot(tab)
|
|
131
172
|
})
|
|
132
173
|
|
|
133
174
|
root.querySelectorAll('[data-space]').forEach((panel) => {
|
|
@@ -452,6 +493,10 @@ onMount(() => {
|
|
|
452
493
|
tints = spaces.map(space => space.tint)
|
|
453
494
|
if (ids.length === 0) return
|
|
454
495
|
|
|
496
|
+
// Before the first paint, so nothing animates on the default timing once and
|
|
497
|
+
// the shared one afterwards.
|
|
498
|
+
publishTiming()
|
|
499
|
+
|
|
455
500
|
const startIndex = clampIndex(config.index || 0, ids.length)
|
|
456
501
|
const saved = restored()
|
|
457
502
|
const initial = saved === -1 ? startIndex : saved
|
|
@@ -560,11 +605,19 @@ defineExpose({
|
|
|
560
605
|
linear-gradient(180deg, rgba(255, 255, 255, 0.34) 0%, rgba(255, 255, 255, 0) 32%),
|
|
561
606
|
linear-gradient(180deg, var(--stx-space-from) 0%, var(--stx-space-to) 100%);
|
|
562
607
|
color: var(--stx-space-ink);
|
|
608
|
+
/* One duration and one curve for everything a space change moves.
|
|
609
|
+
This used to be four timings and three easings across the track, the
|
|
610
|
+
palette, the switcher and the space's own buttons — 420ms on one curve,
|
|
611
|
+
420ms on another, 300ms, 150ms — so a swipe read as several things
|
|
612
|
+
happening near each other rather than as one motion. Published as custom
|
|
613
|
+
properties so the pieces cannot drift apart again. */
|
|
614
|
+
--stx-space-settle: 420ms;
|
|
615
|
+
--stx-space-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
|
|
563
616
|
transition:
|
|
564
|
-
--stx-space-from
|
|
565
|
-
--stx-space-to
|
|
566
|
-
--stx-space-ink
|
|
567
|
-
--stx-space-accent
|
|
617
|
+
--stx-space-from var(--stx-space-settle) var(--stx-space-ease),
|
|
618
|
+
--stx-space-to var(--stx-space-settle) var(--stx-space-ease),
|
|
619
|
+
--stx-space-ink var(--stx-space-settle) var(--stx-space-ease),
|
|
620
|
+
--stx-space-accent var(--stx-space-settle) var(--stx-space-ease);
|
|
568
621
|
}
|
|
569
622
|
|
|
570
623
|
@media (prefers-color-scheme: dark) {
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
// The rail along the bottom of an Arc sidebar: one small icon per space, plus
|
|
3
|
-
// an optional trailing "+". It is the discoverable counterpart to the swipe —
|
|
4
|
-
// the gesture is what you use once you know the spaces exist, the rail is how
|
|
5
|
-
// you find out.
|
|
6
|
-
//
|
|
7
|
-
// The buttons are markup only. `<SidebarSpaces>` owns the single click handler
|
|
8
|
-
// and flips `data-space-current`, so the rail never holds its own copy of
|
|
9
|
-
// which space is active — there is exactly one source of truth.
|
|
10
|
-
export const spaces = ($props.spaces || []).map(space => ({
|
|
11
|
-
id: space.id,
|
|
12
|
-
label: space.label || space.id,
|
|
13
|
-
icon: space.icon || 'i-f7-circle',
|
|
14
|
-
}))
|
|
15
|
-
|
|
16
|
-
export const active = $props.active || (spaces[0] && spaces[0].id) || ''
|
|
17
|
-
export const showAdd = $props.showAdd ?? false
|
|
18
|
-
export const addLabel = $props.addLabel || 'New space'
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<div
|
|
22
|
-
data-space-switcher
|
|
23
|
-
class="flex h-[38px] shrink-0 items-center gap-[1px] px-[8px] pb-[6px]"
|
|
24
|
-
role="tablist"
|
|
25
|
-
aria-label="Spaces"
|
|
26
|
-
aria-orientation="horizontal"
|
|
27
|
-
>
|
|
28
|
-
@foreach(spaces as space)
|
|
29
|
-
<button
|
|
30
|
-
type="button"
|
|
31
|
-
data-space-target="{{ space.id }}"
|
|
32
|
-
data-space-current="{{ space.id === active ? 'true' : 'false' }}"
|
|
33
|
-
class="stx-space-tab"
|
|
34
|
-
role="tab"
|
|
35
|
-
aria-selected="{{ space.id === active ? 'true' : 'false' }}"
|
|
36
|
-
aria-label="{{ space.label }}"
|
|
37
|
-
title="{{ space.label }}"
|
|
38
|
-
tabindex="{{ space.id === active ? '0' : '-1' }}"
|
|
39
|
-
>
|
|
40
|
-
<span class="{{ space.icon }} h-[15px] w-[15px]" aria-hidden="true"></span>
|
|
41
|
-
</button>
|
|
42
|
-
@endforeach
|
|
43
|
-
|
|
44
|
-
<span class="flex-1"></span>
|
|
45
|
-
|
|
46
|
-
@if(showAdd)
|
|
47
|
-
<button
|
|
48
|
-
type="button"
|
|
49
|
-
data-space-add
|
|
50
|
-
class="stx-space-tab"
|
|
51
|
-
aria-label="{{ addLabel }}"
|
|
52
|
-
title="{{ addLabel }}"
|
|
53
|
-
>
|
|
54
|
-
<span class="i-f7-plus h-[14px] w-[14px]" aria-hidden="true"></span>
|
|
55
|
-
</button>
|
|
56
|
-
@endif
|
|
57
|
-
</div>
|
|
58
|
-
|
|
59
|
-
<style scoped>
|
|
60
|
-
/* Rail buttons are painted here rather than with utility classes because the
|
|
61
|
-
current one has to pick up `--stx-space-accent`, which changes as you swipe.
|
|
62
|
-
A class swap would have to be re-applied on every transition; an attribute
|
|
63
|
-
rule keyed on `data-space-current` just follows along. */
|
|
64
|
-
.stx-space-tab {
|
|
65
|
-
display: grid;
|
|
66
|
-
place-items: center;
|
|
67
|
-
width: 28px;
|
|
68
|
-
height: 26px;
|
|
69
|
-
border-radius: 7px;
|
|
70
|
-
color: currentColor;
|
|
71
|
-
opacity: 0.4;
|
|
72
|
-
background: transparent;
|
|
73
|
-
transition: opacity 150ms ease, background-color 150ms ease, color 300ms ease;
|
|
74
|
-
cursor: default;
|
|
75
|
-
}
|
|
76
|
-
.stx-space-tab:hover {
|
|
77
|
-
opacity: 0.75;
|
|
78
|
-
background: rgba(255, 255, 255, 0.4);
|
|
79
|
-
}
|
|
80
|
-
.stx-space-tab[data-space-current="true"] {
|
|
81
|
-
opacity: 1;
|
|
82
|
-
color: var(--stx-space-accent, currentColor);
|
|
83
|
-
background: rgba(255, 255, 255, 0.55);
|
|
84
|
-
}
|
|
85
|
-
.stx-space-tab:focus-visible {
|
|
86
|
-
outline: 2px solid var(--stx-space-accent, currentColor);
|
|
87
|
-
outline-offset: 1px;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
@media (prefers-color-scheme: dark) {
|
|
91
|
-
.stx-space-tab:hover {
|
|
92
|
-
background: rgba(255, 255, 255, 0.08);
|
|
93
|
-
}
|
|
94
|
-
.stx-space-tab[data-space-current="true"] {
|
|
95
|
-
background: rgba(255, 255, 255, 0.12);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
</style>
|