@aria-framework/theme 0.4.0 → 0.5.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 +30 -6
- package/build.js +10 -3
- package/package.json +1 -1
- package/src/components/sidebar.css +15 -14
- package/tokens.json +17 -6
- package/web/sidebar.js +6 -0
- package/web/theme.css +21 -17
package/README.md
CHANGED
|
@@ -76,10 +76,12 @@ belongs here; if it knows about your domain, it stays in the app.
|
|
|
76
76
|
|
|
77
77
|
### Component: sidebar rail
|
|
78
78
|
|
|
79
|
-
A
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
A nav rail themed **per scheme** — light rail in light mode, dark rail in dark
|
|
80
|
+
mode (driven by the `--side-*` tokens, including `--side-strong` for emphasis
|
|
81
|
+
text and `--side-accent` for the active bar / icon / focus ring). Mobile-first:
|
|
82
|
+
off-canvas overlay under 768px (hamburger + backdrop + Esc/backdrop-tap close,
|
|
83
|
+
focus trapped via `inert`); an in-flow rail on desktop with a persisted collapse
|
|
84
|
+
to a 64px icon rail. The package ships the **CSS** (in `theme.css`) and the
|
|
83
85
|
**behavior** (`sidebar.js`); the app ships the **markup** against this contract:
|
|
84
86
|
|
|
85
87
|
```html
|
|
@@ -118,8 +120,12 @@ Notes:
|
|
|
118
120
|
script: `if (localStorage.getItem('<key>') === '1') document.documentElement.classList.add('sidebar-collapsed')`.
|
|
119
121
|
- State classes live on `<html>`: `.sidebar-open` (mobile overlay),
|
|
120
122
|
`.sidebar-collapsed` (desktop icon rail).
|
|
121
|
-
- Sidebar tokens are themeable in `tokens.json` under `"sidebar"` (
|
|
122
|
-
`--side-*`
|
|
123
|
+
- Sidebar tokens are themeable in `tokens.json` under `"sidebar"` (per-scheme
|
|
124
|
+
`light`/`dark` sub-objects, emitted as `--side-*` in `:root` and the dark
|
|
125
|
+
block). Icons in the examples are Bootstrap Icons, but any icon font works.
|
|
126
|
+
- If your app paints something with `var(--side-*)` that should IGNORE the
|
|
127
|
+
scheme (e.g. a permanently-dark login hero panel), use fixed values there —
|
|
128
|
+
since 0.5.0 these tokens follow the active scheme.
|
|
123
129
|
|
|
124
130
|
## Mobile (Expo / React Native)
|
|
125
131
|
|
|
@@ -159,3 +165,21 @@ Token change → edit `tokens.json`, run `npm run build`, commit the regenerated
|
|
|
159
165
|
hand-maintained web/mobile.
|
|
160
166
|
- **v0.2.0** — added `tokens.json` + `build.js` generator (single source of record;
|
|
161
167
|
web + mobile generated from one file). Unified the drifted dark `--line-strong`.
|
|
168
|
+
- **v0.3.0** — renamed to `@aria-framework/theme` and published to npm. Mobile
|
|
169
|
+
sources now compile to `dist/` (.js + `.d.ts`, via a `prepare` script) so RN
|
|
170
|
+
apps can import the package out-of-tree; `exports` point at the built files.
|
|
171
|
+
- **v0.4.0** — first shell component: the **sidebar rail** (CSS + `--side-*`
|
|
172
|
+
tokens + `sidebar.js` behavior with a per-app `data-storage-key`; markup
|
|
173
|
+
contract above). Build now assembles generated tokens → `src/base.css` →
|
|
174
|
+
`src/components/*.css`. Sidebar carries its own focus-visible contrast rule.
|
|
175
|
+
- **v0.4.1** — `sidebar.js` double-include guard (a second script tag no longer
|
|
176
|
+
registers duplicate listeners); this changelog added.
|
|
177
|
+
- **v0.5.0** — sidebar rail is now **per-scheme** (light rail in light mode).
|
|
178
|
+
`tokens.json` `"sidebar"` gained `light`/`dark` sub-objects and two new tokens:
|
|
179
|
+
`strong` (emphasis text, replaces hardcoded `#fff`) and `accent` (active bar /
|
|
180
|
+
icon / focus ring). Apps that used `--side-*` for deliberately-always-dark
|
|
181
|
+
surfaces must pin fixed colors instead.
|
|
182
|
+
|
|
183
|
+
> **0.x semver reminder:** `^0.4.0` matches `<0.5.0` only — consumers pick up
|
|
184
|
+
> patches via `npm update`, but every **minor** bump (0.4 → 0.5) requires
|
|
185
|
+
> editing the range in each consuming app.
|
package/build.js
CHANGED
|
@@ -22,6 +22,9 @@ const hexToRgb = (hex) => {
|
|
|
22
22
|
for (const key of ["palettes", "sidebar", "status", "priority", "space"]) {
|
|
23
23
|
if (!T[key]) throw new Error(`tokens.json is missing the "${key}" section (required since theme 0.4.0)`);
|
|
24
24
|
}
|
|
25
|
+
if (!T.sidebar.light || !T.sidebar.dark) {
|
|
26
|
+
throw new Error('tokens.json "sidebar" must have "light" and "dark" sub-objects (per-scheme since theme 0.5.0)');
|
|
27
|
+
}
|
|
25
28
|
|
|
26
29
|
/* ── web/theme.css ────────────────────────────────────────────────────────── */
|
|
27
30
|
const cssTokens = (p) => ` --brand:${p.brand}; --brand-deep:${p.brandDeep}; --brand-bright:${p.brandBright}; --brand-rgb:${hexToRgb(p.brand)};
|
|
@@ -31,6 +34,9 @@ const cssTokens = (p) => ` --brand:${p.brand}; --brand-deep:${p.brandDeep}; --b
|
|
|
31
34
|
--shadow:${p.shadowWeb};
|
|
32
35
|
--shadow-lg:${p.shadowLgWeb};`;
|
|
33
36
|
|
|
37
|
+
const sideTokens = (s) => ` --side-bg1:${s.bg1}; --side-bg2:${s.bg2}; --side-tx:${s.tx}; --side-muted:${s.muted}; --side-strong:${s.strong};
|
|
38
|
+
--side-active:${s.active}; --side-line:${s.line}; --side-card:${s.card}; --side-accent:${s.accent};`;
|
|
39
|
+
|
|
34
40
|
const themeCss = `${GEN_CSS}
|
|
35
41
|
/* @aria-framework/theme — Field Ops Console design system (web).
|
|
36
42
|
Drop-in token layer for any Bootstrap 5.3 app. Load order in <head>:
|
|
@@ -42,12 +48,13 @@ const themeCss = `${GEN_CSS}
|
|
|
42
48
|
--font-sans:${FONT_SANS};
|
|
43
49
|
--font-mono:${FONT_MONO};
|
|
44
50
|
${cssTokens(T.palettes.light)}
|
|
45
|
-
/* Sidebar rail (
|
|
46
|
-
|
|
47
|
-
--side-active:${T.sidebar.active}; --side-line:${T.sidebar.line}; --side-card:${T.sidebar.card};
|
|
51
|
+
/* Sidebar rail (light) */
|
|
52
|
+
${sideTokens(T.sidebar.light)}
|
|
48
53
|
}
|
|
49
54
|
[data-bs-theme="dark"]{
|
|
50
55
|
${cssTokens(T.palettes.dark)}
|
|
56
|
+
/* Sidebar rail (dark) */
|
|
57
|
+
${sideTokens(T.sidebar.dark)}
|
|
51
58
|
}`;
|
|
52
59
|
|
|
53
60
|
/* ── mobile/tokens.ts ─────────────────────────────────────────────────────── */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aria-framework/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Aria App Framework — theme module. Field Ops Console design system: tokens.json single source of record + a generator that emits a Bootstrap 5.3 web theme (theme.css + self-hosted fonts) and a React Native palette/provider/primitives. Light + dark.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* ── Component: sidebar rail (.s-side) ───────────────────────────────────────
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
Nav rail, themed per scheme via the --side-* tokens (light rail in light
|
|
3
|
+
mode, dark rail in dark mode). Mobile-first: off-canvas overlay by default;
|
|
4
|
+
the ≥768px block puts it back in flow as the desktop rail with a collapsible
|
|
5
|
+
icon-rail mode. Behavior lives in sidebar.js (served beside theme.css).
|
|
5
6
|
|
|
6
7
|
MARKUP CONTRACT (the app renders this; see package README):
|
|
7
8
|
#wrapper > .s-side + #page-content-wrapper
|
|
@@ -37,7 +38,7 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
37
38
|
background:linear-gradient(140deg, var(--brand-bright), var(--brand-deep)); color:#fff; font-weight:800; font-size:17px;
|
|
38
39
|
box-shadow:0 0 0 1px rgba(255,255,255,.06), 0 6px 16px -6px var(--brand);
|
|
39
40
|
}
|
|
40
|
-
.s-name{ display:block; color
|
|
41
|
+
.s-name{ display:block; color:var(--side-strong); font-weight:700; font-size:16px; letter-spacing:-.01em; line-height:1; }
|
|
41
42
|
.s-ver{ display:block; font-family:var(--font-mono); font-size:11px; color:var(--side-muted); margin-top:3px; }
|
|
42
43
|
|
|
43
44
|
.s-nav{ flex:1; overflow-y:auto; padding:4px 12px 12px; }
|
|
@@ -52,10 +53,10 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
52
53
|
white-space:nowrap;
|
|
53
54
|
}
|
|
54
55
|
.s-link i{ font-size:16px; width:18px; text-align:center; opacity:.85; }
|
|
55
|
-
.s-link:hover{ background:var(--side-card); color
|
|
56
|
-
.s-link.active{ background:var(--side-active); color
|
|
57
|
-
.s-link.active::before{ content:""; position:absolute; left:-12px; top:8px; bottom:8px; width:3px; border-radius:0 3px 3px 0; background:var(--
|
|
58
|
-
.s-link.active i{ opacity:1; color:var(--
|
|
56
|
+
.s-link:hover{ background:var(--side-card); color:var(--side-strong); }
|
|
57
|
+
.s-link.active{ background:var(--side-active); color:var(--side-strong); }
|
|
58
|
+
.s-link.active::before{ content:""; position:absolute; left:-12px; top:8px; bottom:8px; width:3px; border-radius:0 3px 3px 0; background:var(--side-accent); }
|
|
59
|
+
.s-link.active i{ opacity:1; color:var(--side-accent); }
|
|
59
60
|
|
|
60
61
|
.s-me{
|
|
61
62
|
margin:10px 12px 14px; padding:10px 12px; border-radius:11px; display:flex; align-items:center; gap:10px;
|
|
@@ -66,11 +67,11 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
66
67
|
background:linear-gradient(140deg, var(--brand), var(--brand-deep)); color:#fff; font-weight:700; font-size:13px;
|
|
67
68
|
}
|
|
68
69
|
.s-me-who{ min-width:0; line-height:1.25; flex:1; }
|
|
69
|
-
.s-me-who b{ display:block; color
|
|
70
|
+
.s-me-who b{ display:block; color:var(--side-strong); font-size:13.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
70
71
|
.s-me-who span{ font-family:var(--font-mono); font-size:10.5px; color:var(--side-muted); text-transform:uppercase; letter-spacing:.06em; }
|
|
71
72
|
.s-out-form{ margin:0; }
|
|
72
73
|
.s-out{ border:0; background:transparent; color:var(--side-muted); font-size:16px; line-height:1; padding:4px; cursor:pointer; }
|
|
73
|
-
.s-out:hover{ color
|
|
74
|
+
.s-out:hover{ color:var(--side-strong); }
|
|
74
75
|
|
|
75
76
|
/* Collapse toggle — the sidebar-header chevron (shown on desktop) and the
|
|
76
77
|
topbar hamburger (shown on mobile, opens the overlay). */
|
|
@@ -78,13 +79,13 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
78
79
|
flex:0 0 auto; margin-left:auto; width:28px; height:28px; padding:0; border:0; cursor:pointer;
|
|
79
80
|
background:transparent; color:var(--side-muted); border-radius:7px; display:none; place-items:center; font-size:15px;
|
|
80
81
|
}
|
|
81
|
-
.s-collapse:hover{ background:var(--side-card); color
|
|
82
|
+
.s-collapse:hover{ background:var(--side-card); color:var(--side-strong); }
|
|
82
83
|
.s-collapse i{ display:block; transition:transform .18s ease; } /* rotate the <i> (centre origin) — no SVG-pivot bug */
|
|
83
84
|
html.sidebar-collapsed .s-collapse i{ transform:rotate(180deg); } /* points right = expand */
|
|
84
85
|
|
|
85
|
-
/*
|
|
86
|
-
var(--brand), which
|
|
87
|
-
.s-link:focus-visible, .s-out:focus-visible, .s-brand-link:focus-visible, .s-collapse:focus-visible { outline-color: var(--
|
|
86
|
+
/* Keyboard-focus ring in the rail's own accent — the base a11y rule uses
|
|
87
|
+
var(--brand), which can vanish against the rail background. */
|
|
88
|
+
.s-link:focus-visible, .s-out:focus-visible, .s-brand-link:focus-visible, .s-collapse:focus-visible { outline-color: var(--side-accent); }
|
|
88
89
|
|
|
89
90
|
.topbar-toggle{
|
|
90
91
|
flex:0 0 auto; width:38px; height:38px; border-radius:10px; border:1px solid var(--line);
|
package/tokens.json
CHANGED
|
@@ -33,12 +33,23 @@
|
|
|
33
33
|
"status": { "new": "#2563eb", "open": "#0e8fa8", "pending": "#d9870b", "resolved": "#0e9e6e", "closed": "#7a848b" },
|
|
34
34
|
"priority": { "low": "#9aa6ae", "medium": "#2563eb", "high": "#ea580c", "critical": "#dc2626" },
|
|
35
35
|
"space": { "xs": 4, "sm": 8, "md": 12, "lg": 16, "xl": 24 },
|
|
36
|
-
"$sidebarComment": "Sidebar rail tokens (web sidebar component)
|
|
36
|
+
"$sidebarComment": "Sidebar rail tokens (web sidebar component), PER-SCHEME (--side-* in :root = light, overridden in [data-bs-theme=dark]). 'strong' = emphasis text (brand name, active/hover labels); 'accent' = active bar / active icon / focus ring — brand-bright pops on the dark rail, plain brand reads better on the light one.",
|
|
37
37
|
"sidebar": {
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
"light": {
|
|
39
|
+
"bg1": "#FBFCFA", "bg2": "#F3F4F0",
|
|
40
|
+
"tx": "#4A555E", "muted": "#8C969D", "strong": "#18222A",
|
|
41
|
+
"active": "rgba(14,122,110,.10)",
|
|
42
|
+
"line": "rgba(24,34,42,.08)",
|
|
43
|
+
"card": "rgba(24,34,42,.05)",
|
|
44
|
+
"accent": "#0E7A6E"
|
|
45
|
+
},
|
|
46
|
+
"dark": {
|
|
47
|
+
"bg1": "#0E1417", "bg2": "#141C21",
|
|
48
|
+
"tx": "#BAC3C8", "muted": "#69767D", "strong": "#FFFFFF",
|
|
49
|
+
"active": "rgba(21,184,166,.13)",
|
|
50
|
+
"line": "rgba(255,255,255,.07)",
|
|
51
|
+
"card": "rgba(255,255,255,.045)",
|
|
52
|
+
"accent": "#15B8A6"
|
|
53
|
+
}
|
|
43
54
|
}
|
|
44
55
|
}
|
package/web/sidebar.js
CHANGED
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
* two layers can't drift. CSP-safe: external, self-hosted, no inline script.
|
|
22
22
|
*/
|
|
23
23
|
(function () {
|
|
24
|
+
// Double-include guard: a second copy (e.g. two layout partials both adding
|
|
25
|
+
// the script tag) would register a second delegated listener, making every
|
|
26
|
+
// toggle fire twice — a silent, error-free dead sidebar. First copy wins.
|
|
27
|
+
if (window.__ariaSidebar) return;
|
|
28
|
+
window.__ariaSidebar = true;
|
|
29
|
+
|
|
24
30
|
// currentScript is only set during initial execution — capture before deferring.
|
|
25
31
|
var script = document.currentScript;
|
|
26
32
|
var KEY = (script && script.dataset && script.dataset.storageKey) || 'aria-sidebar-collapsed';
|
package/web/theme.css
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
--text:#18222A; --text-soft:#5C6770; --text-faint:#929BA1;
|
|
15
15
|
--shadow:0 1px 2px rgba(16,24,28,.05), 0 4px 14px -6px rgba(16,24,28,.12);
|
|
16
16
|
--shadow-lg:0 18px 50px -18px rgba(16,24,28,.30);
|
|
17
|
-
/* Sidebar rail (
|
|
18
|
-
--side-bg1:#
|
|
19
|
-
--side-active:rgba(
|
|
17
|
+
/* Sidebar rail (light) */
|
|
18
|
+
--side-bg1:#FBFCFA; --side-bg2:#F3F4F0; --side-tx:#4A555E; --side-muted:#8C969D; --side-strong:#18222A;
|
|
19
|
+
--side-active:rgba(14,122,110,.10); --side-line:rgba(24,34,42,.08); --side-card:rgba(24,34,42,.05); --side-accent:#0E7A6E;
|
|
20
20
|
}
|
|
21
21
|
[data-bs-theme="dark"]{
|
|
22
22
|
--brand:#16B5A3; --brand-deep:#0E7A6E; --brand-bright:#2FE0CC; --brand-rgb:22,181,163;
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
--text:#E7EDEF; --text-soft:#9BA7AE; --text-faint:#69767D;
|
|
26
26
|
--shadow:0 1px 2px rgba(0,0,0,.3), 0 8px 24px -10px rgba(0,0,0,.6);
|
|
27
27
|
--shadow-lg:0 24px 60px -18px rgba(0,0,0,.8);
|
|
28
|
+
/* Sidebar rail (dark) */
|
|
29
|
+
--side-bg1:#0E1417; --side-bg2:#141C21; --side-tx:#BAC3C8; --side-muted:#69767D; --side-strong:#FFFFFF;
|
|
30
|
+
--side-active:rgba(21,184,166,.13); --side-line:rgba(255,255,255,.07); --side-card:rgba(255,255,255,.045); --side-accent:#15B8A6;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
/* ── Base — static layer (fonts, Bootstrap mapping, edge-clarity, buttons, a11y).
|
|
@@ -92,9 +95,10 @@ textarea:focus-visible, [tabindex]:focus-visible {
|
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
/* ── Component: sidebar rail (.s-side) ───────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
Nav rail, themed per scheme via the --side-* tokens (light rail in light
|
|
99
|
+
mode, dark rail in dark mode). Mobile-first: off-canvas overlay by default;
|
|
100
|
+
the ≥768px block puts it back in flow as the desktop rail with a collapsible
|
|
101
|
+
icon-rail mode. Behavior lives in sidebar.js (served beside theme.css).
|
|
98
102
|
|
|
99
103
|
MARKUP CONTRACT (the app renders this; see package README):
|
|
100
104
|
#wrapper > .s-side + #page-content-wrapper
|
|
@@ -130,7 +134,7 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
130
134
|
background:linear-gradient(140deg, var(--brand-bright), var(--brand-deep)); color:#fff; font-weight:800; font-size:17px;
|
|
131
135
|
box-shadow:0 0 0 1px rgba(255,255,255,.06), 0 6px 16px -6px var(--brand);
|
|
132
136
|
}
|
|
133
|
-
.s-name{ display:block; color
|
|
137
|
+
.s-name{ display:block; color:var(--side-strong); font-weight:700; font-size:16px; letter-spacing:-.01em; line-height:1; }
|
|
134
138
|
.s-ver{ display:block; font-family:var(--font-mono); font-size:11px; color:var(--side-muted); margin-top:3px; }
|
|
135
139
|
|
|
136
140
|
.s-nav{ flex:1; overflow-y:auto; padding:4px 12px 12px; }
|
|
@@ -145,10 +149,10 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
145
149
|
white-space:nowrap;
|
|
146
150
|
}
|
|
147
151
|
.s-link i{ font-size:16px; width:18px; text-align:center; opacity:.85; }
|
|
148
|
-
.s-link:hover{ background:var(--side-card); color
|
|
149
|
-
.s-link.active{ background:var(--side-active); color
|
|
150
|
-
.s-link.active::before{ content:""; position:absolute; left:-12px; top:8px; bottom:8px; width:3px; border-radius:0 3px 3px 0; background:var(--
|
|
151
|
-
.s-link.active i{ opacity:1; color:var(--
|
|
152
|
+
.s-link:hover{ background:var(--side-card); color:var(--side-strong); }
|
|
153
|
+
.s-link.active{ background:var(--side-active); color:var(--side-strong); }
|
|
154
|
+
.s-link.active::before{ content:""; position:absolute; left:-12px; top:8px; bottom:8px; width:3px; border-radius:0 3px 3px 0; background:var(--side-accent); }
|
|
155
|
+
.s-link.active i{ opacity:1; color:var(--side-accent); }
|
|
152
156
|
|
|
153
157
|
.s-me{
|
|
154
158
|
margin:10px 12px 14px; padding:10px 12px; border-radius:11px; display:flex; align-items:center; gap:10px;
|
|
@@ -159,11 +163,11 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
159
163
|
background:linear-gradient(140deg, var(--brand), var(--brand-deep)); color:#fff; font-weight:700; font-size:13px;
|
|
160
164
|
}
|
|
161
165
|
.s-me-who{ min-width:0; line-height:1.25; flex:1; }
|
|
162
|
-
.s-me-who b{ display:block; color
|
|
166
|
+
.s-me-who b{ display:block; color:var(--side-strong); font-size:13.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
163
167
|
.s-me-who span{ font-family:var(--font-mono); font-size:10.5px; color:var(--side-muted); text-transform:uppercase; letter-spacing:.06em; }
|
|
164
168
|
.s-out-form{ margin:0; }
|
|
165
169
|
.s-out{ border:0; background:transparent; color:var(--side-muted); font-size:16px; line-height:1; padding:4px; cursor:pointer; }
|
|
166
|
-
.s-out:hover{ color
|
|
170
|
+
.s-out:hover{ color:var(--side-strong); }
|
|
167
171
|
|
|
168
172
|
/* Collapse toggle — the sidebar-header chevron (shown on desktop) and the
|
|
169
173
|
topbar hamburger (shown on mobile, opens the overlay). */
|
|
@@ -171,13 +175,13 @@ html.sidebar-open #sidebar-backdrop{ opacity:1; visibility:visible; }
|
|
|
171
175
|
flex:0 0 auto; margin-left:auto; width:28px; height:28px; padding:0; border:0; cursor:pointer;
|
|
172
176
|
background:transparent; color:var(--side-muted); border-radius:7px; display:none; place-items:center; font-size:15px;
|
|
173
177
|
}
|
|
174
|
-
.s-collapse:hover{ background:var(--side-card); color
|
|
178
|
+
.s-collapse:hover{ background:var(--side-card); color:var(--side-strong); }
|
|
175
179
|
.s-collapse i{ display:block; transition:transform .18s ease; } /* rotate the <i> (centre origin) — no SVG-pivot bug */
|
|
176
180
|
html.sidebar-collapsed .s-collapse i{ transform:rotate(180deg); } /* points right = expand */
|
|
177
181
|
|
|
178
|
-
/*
|
|
179
|
-
var(--brand), which
|
|
180
|
-
.s-link:focus-visible, .s-out:focus-visible, .s-brand-link:focus-visible, .s-collapse:focus-visible { outline-color: var(--
|
|
182
|
+
/* Keyboard-focus ring in the rail's own accent — the base a11y rule uses
|
|
183
|
+
var(--brand), which can vanish against the rail background. */
|
|
184
|
+
.s-link:focus-visible, .s-out:focus-visible, .s-brand-link:focus-visible, .s-collapse:focus-visible { outline-color: var(--side-accent); }
|
|
181
185
|
|
|
182
186
|
.topbar-toggle{
|
|
183
187
|
flex:0 0 auto; width:38px; height:38px; border-radius:10px; border:1px solid var(--line);
|