Stage 4a: context menu is pure 7.css; retire the lookalike and the false claim
The menu now renders 7.css's real contract — ul[role=menu] > li[role=menuitem] > button for actions, input[type=checkbox] + label for the checkable column toggles, and dividers as .has-divider on the PRECEDING item (7.css draws them as an ::after bevel, not as a node; the service's separator entries collapse onto their predecessor at render time). With the markup right, the framework supplies everything the hand-written version imitated or lacked: the left gutter stripe, bevelled separators, the gradient selection band, the checked box, and disabled/submenu styling we previously had none of. Deleted: the w7-menu lookalike (both the button-slab stripper in `skin` and the whole hand-authored Aero set in `skinfix`) and its w7-menu/w7-menu-sep hooks. The "7.css has no menu component" claims in styles.css and architecture.md were FALSE — dist/gui/menu.css is a full component; the old markup (div > button) simply matched none of it. Both corrected to state the real contract and the trap. Keyboard now follows the platform instead of a highlight class: arrows move REAL focus between items — 7.css styles :focus-visible identically to :hover, and keyboard-initiated programmatic focus counts — and Enter/Space activate buttons natively; only labels need a synthetic click. This also makes the menu honest to screen readers. Dark mode: menu.css hardcodes light literals (color:initial, white bevel halves, the #b3d3f9 checked-box ring); restated in skinfix's dark arm like the ListView's. Also fixed, caught BY the harness: the Stage 3 frame sizing rule was keyed on bare `.window`, forcing 100vh height onto every 7.css window (the harness's demo windows blew up to full page). It is now keyed on `w7-frame`, a hook only the three real window roots carry. Verified zoomed in light and dark against 7.css's reference markup: gutter, dividers, checkmark box, grey right-aligned accelerator all match; bundle 490.92 kB. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+15
-15
@@ -304,22 +304,22 @@ one) as ~20 `--w7-*` overrides, duplicated forced + system like every other
|
||||
dark block. `--w7-font` is restated as a px `font` shorthand so `data-text`
|
||||
still scales and 7.css's `pt` shorthand stops resetting weight/line-height.
|
||||
|
||||
**Markup hooks** are additive and inert under `modern`: `role="progressbar"`
|
||||
(+aria) on the transfer bars, `has-scrollbar` on scroll containers (7.css only
|
||||
styles `::-webkit-scrollbar` under that class), `w7-toolbar` on the three
|
||||
header rows (their buttons go flat — Explorer's command bar was flat, unlike
|
||||
dialog push buttons), `w7-btn` on the 10 dialog action buttons, `w7-menu` (+
|
||||
`w7-menu-sep`) on the HTML context menu.
|
||||
**Markup hooks**: `role="progressbar"` (+aria) on the transfer bars,
|
||||
`has-scrollbar` on scroll containers (7.css only styles `::-webkit-scrollbar`
|
||||
under that class), `w7-toolbar` on the three header rows (their buttons go
|
||||
flat — Explorer's command bar was flat, unlike dialog push buttons), `w7-btn`
|
||||
on the 10 dialog action buttons.
|
||||
|
||||
**Context menu**: 7.css ships no menu component, so the Aero menu is
|
||||
hand-authored across both layers — the row `<button>`s lose their push-button
|
||||
slab in `skin` (needs `revert-layer`), and the slab/border/selection-band paint
|
||||
is stated in `skinfix`. It is built ONLY from existing `--w7-*` vars (never
|
||||
literal hex), so the hand-written dark Win7 palette recolours it for free —
|
||||
the same constraint the dark block documents for itself. The hover and
|
||||
keyboard-cursor highlights are deliberately given the same band: the component
|
||||
marks the keyboard cursor with `bg-accent`, which is overridden alongside
|
||||
`:hover` so the two are indistinguishable, as in a real Win7 menu.
|
||||
**Context menu**: pure 7.css via its real menu contract —
|
||||
`ul[role=menu] > li[role=menuitem] > button` for actions,
|
||||
`input[type=checkbox] + label` for the checkable column toggles, dividers as
|
||||
`.has-divider` on the PRECEDING item (an `::after` bevel, not a node). The
|
||||
contract is exact: an earlier iteration rendered `div > button`, matched
|
||||
nothing, and wrongly concluded "7.css has no menu component" — it has a full
|
||||
one (`dist/gui/menu.css`), including the gutter stripe, disabled state and
|
||||
submenu arrows. Keyboard moves REAL focus between items (7.css styles
|
||||
`:focus-visible` like `:hover`); Enter/Space activate buttons natively. Only
|
||||
menu.css's hardcoded light literals are restated for dark, in `skinfix`.
|
||||
|
||||
**Font**: Segoe UI is absent off Windows and not redistributable. Noto Sans —
|
||||
the substitute 7.css itself names — is vendored (OFL-1.1, from
|
||||
|
||||
+17
-17
@@ -199,23 +199,23 @@
|
||||
</ul>
|
||||
</figure>
|
||||
<figure class="cell" style="margin:0">
|
||||
<figcaption>ours — div > button + hand-written CSS</figcaption>
|
||||
<div class="w7-menu" style="width:224px">
|
||||
<button style="display:flex;width:100%;gap:8px;padding:3px 8px;text-align:left">
|
||||
<span style="width:14px"></span><span style="flex:1">Send to Other Panel</span>
|
||||
</button>
|
||||
<button style="display:flex;width:100%;gap:8px;padding:3px 8px;text-align:left">
|
||||
<span style="width:14px"></span><span style="flex:1">Open</span>
|
||||
</button>
|
||||
<div class="w7-menu-sep" style="border-top:1px solid var(--w7-el-bd);margin:2px 0"></div>
|
||||
<button style="display:flex;width:100%;gap:8px;padding:3px 8px;text-align:left">
|
||||
<span style="width:14px"></span><span style="flex:1">Rename</span>
|
||||
</button>
|
||||
<button style="display:flex;width:100%;gap:8px;padding:3px 8px;text-align:left">
|
||||
<span style="width:14px"></span><span style="flex:1">Edit</span>
|
||||
<span style="color:var(--w7-el-c-d)">F4</span>
|
||||
</button>
|
||||
</div>
|
||||
<figcaption>ours — context-menu.ts output (buttons, divider, accelerator, checkables)</figcaption>
|
||||
<ul role="menu" style="width:200px">
|
||||
<li role="menuitem"><button type="button">Send to Other Panel</button></li>
|
||||
<li role="menuitem"><button type="button">Open</button></li>
|
||||
<li role="menuitem"><button type="button">Edit <span>F4</span></button></li>
|
||||
<li role="menuitem" class="has-divider"><button type="button">Rename</button></li>
|
||||
<li role="menuitem"><button type="button">Copy Path</button></li>
|
||||
<li role="menuitem" class="has-divider"><button type="button">Delete</button></li>
|
||||
<li role="menuitem">
|
||||
<input type="checkbox" id="ours-col-size" checked tabindex="-1" />
|
||||
<label for="ours-col-size">Size</label>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<input type="checkbox" id="ours-col-kind" tabindex="-1" />
|
||||
<label for="ours-col-kind">Kind</label>
|
||||
</li>
|
||||
</ul>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<!-- .window.glass is 7.css's Aero frame (radius, 1px border, inset highlight,
|
||||
drop shadow, backdrop blur); .active is what un-greys the title bar and its
|
||||
control glyphs — every .title-bar-controls rule is keyed on it. -->
|
||||
<div class="window glass flex h-screen flex-col" [class.active]="windowFocused()">
|
||||
<div class="window glass w7-frame flex h-screen flex-col" [class.active]="windowFocused()">
|
||||
@if (!rpc.available) {
|
||||
<div class="flex flex-1 items-center justify-center">
|
||||
<div class="max-w-sm rounded-lg border border-border bg-card p-6 text-center shadow-sm">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="window glass flex h-screen flex-col" [class.active]="windowFocused()">
|
||||
<div class="window glass w7-frame flex h-screen flex-col" [class.active]="windowFocused()">
|
||||
<!-- Frameless window. Fixed-size modal, so Close only. -->
|
||||
<app-title-bar
|
||||
[title]="i18n.t('connMgr.title')"
|
||||
|
||||
+83
-65
@@ -1,69 +1,70 @@
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
signal,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import { NgIcon } from '@ng-icons/core';
|
||||
import { Component, ElementRef, computed, effect, inject, signal, viewChild } from '@angular/core';
|
||||
import { ContextMenuService, type MenuItem } from './context-menu.service';
|
||||
import { FocusService } from './focus.service';
|
||||
|
||||
/** Rough per-item metrics, used only to decide whether the menu must flip. */
|
||||
const ITEM_H = 26;
|
||||
const SEPARATOR_H = 7;
|
||||
const MENU_W = 224; // w-56
|
||||
const ITEM_H = 24; // 7.css: 4px padding x2 + ~15px line
|
||||
const DIVIDER_H = 7; // .has-divider:after: 2px bevel + margins
|
||||
const MENU_W = 200;
|
||||
const EDGE_GAP = 6;
|
||||
|
||||
/** One rendered <li>: separators collapse onto the item BEFORE them. */
|
||||
interface MenuRow {
|
||||
item: MenuItem;
|
||||
hasDivider: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The panel right-click menu. Replaces electrobun's native ContextMenu, which
|
||||
* is a no-op on Linux — see ContextMenuService for the why. Emits the same
|
||||
* `ctx-*` / `col:*` action strings the native menu did.
|
||||
*
|
||||
* The markup is 7.css's menu contract EXACTLY — `ul[role=menu] >
|
||||
* li[role=menuitem] > button` for actions, `> input[type=checkbox] + label`
|
||||
* for the checkable column toggles, and dividers as `.has-divider` on the
|
||||
* PRECEDING item (7.css draws them as an ::after bevel, not as a node). Get
|
||||
* any of that wrong and 7.css silently styles nothing: an earlier version
|
||||
* rendered div > button, concluded "7.css has no menu component", and
|
||||
* hand-built a lookalike. It does have one; the selectors just never matched.
|
||||
*
|
||||
* Keyboard follows the platform: arrows move REAL focus between items (7.css
|
||||
* styles :focus-visible identically to :hover), Enter/Space on a button
|
||||
* activate it natively; only labels need a synthetic click.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-context-menu',
|
||||
imports: [NgIcon],
|
||||
template: `
|
||||
@if (menu.request(); as req) {
|
||||
<!-- click-outside catcher; mousedown so it beats the panel's own handlers -->
|
||||
<div class="fixed inset-0 z-50" (mousedown)="close()" (contextmenu)="onBackdropContext($event)"></div>
|
||||
<div
|
||||
<ul
|
||||
#menuEl
|
||||
role="menu"
|
||||
tabindex="-1"
|
||||
(keydown)="onKeydown($event)"
|
||||
[style.left.px]="position().x"
|
||||
[style.top.px]="position().y"
|
||||
class="w7-menu fixed z-50 w-56 rounded-lg border border-border bg-card py-1 text-card-foreground shadow-xl outline-none"
|
||||
[style.width.px]="menuWidth"
|
||||
class="fixed z-50 outline-none"
|
||||
>
|
||||
@for (item of req.items; track $index) {
|
||||
@if (item.type === 'separator') {
|
||||
<div class="w7-menu-sep my-1 border-t border-border"></div>
|
||||
@for (row of rows(); track $index) {
|
||||
<li role="menuitem" [class.has-divider]="row.hasDivider" [title]="row.item.tooltip ?? ''">
|
||||
@if (row.item.checked !== undefined) {
|
||||
<input type="checkbox" [id]="'ctx-check-' + $index" [checked]="row.item.checked" tabindex="-1" />
|
||||
<label [for]="'ctx-check-' + $index" tabindex="-1" (click)="pick(row.item, $event)">
|
||||
{{ row.item.label }}
|
||||
</label>
|
||||
} @else {
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
[title]="item.tooltip ?? ''"
|
||||
[class.bg-accent]="$index === activeIndex()"
|
||||
(mouseenter)="activeIndex.set($index)"
|
||||
(click)="pick(item)"
|
||||
class="flex w-full items-center gap-2 px-3 py-1 text-left text-(length:--path-font) hover:bg-accent"
|
||||
>
|
||||
<span class="w-3.5 shrink-0">
|
||||
@if (item.checked) {
|
||||
<ng-icon name="mynaCheck" size="0.85rem" class="text-primary" />
|
||||
}
|
||||
</span>
|
||||
<span class="flex-1 truncate">{{ item.label }}</span>
|
||||
@if (item.accelerator) {
|
||||
<span class="shrink-0 text-muted-foreground">{{ item.accelerator }}</span>
|
||||
<button type="button" (click)="pick(row.item)">
|
||||
{{ row.item.label }}
|
||||
@if (row.item.accelerator) {
|
||||
<span>{{ row.item.accelerator }}</span>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</div>
|
||||
</ul>
|
||||
}
|
||||
`,
|
||||
// Closing on scroll/blur matches native menu behavior: the anchor row moves
|
||||
@@ -77,24 +78,35 @@ const EDGE_GAP = 6;
|
||||
export class ContextMenu {
|
||||
readonly menu = inject(ContextMenuService);
|
||||
private readonly focus = inject(FocusService);
|
||||
private readonly menuEl = viewChild<ElementRef<HTMLDivElement>>('menuEl');
|
||||
private readonly menuEl = viewChild<ElementRef<HTMLUListElement>>('menuEl');
|
||||
|
||||
readonly activeIndex = signal(-1);
|
||||
protected readonly menuWidth = MENU_W;
|
||||
|
||||
/** Model items → rendered rows: a separator becomes has-divider on its predecessor. */
|
||||
readonly rows = computed<MenuRow[]>(() => {
|
||||
const items = this.menu.request()?.items ?? [];
|
||||
const rows: MenuRow[] = [];
|
||||
for (const item of items) {
|
||||
if (item.type === 'separator') {
|
||||
if (rows.length) rows[rows.length - 1].hasDivider = true;
|
||||
} else {
|
||||
rows.push({ item, hasDivider: false });
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
});
|
||||
|
||||
/** Click point, flipped/clamped so the menu never overflows the viewport. */
|
||||
readonly position = computed(() => {
|
||||
const req = this.menu.request();
|
||||
if (!req) return { x: 0, y: 0 };
|
||||
const rows = this.rows();
|
||||
const height =
|
||||
req.items.reduce((sum, i) => sum + (i.type === 'separator' ? SEPARATOR_H : ITEM_H), 0) + 8;
|
||||
rows.length * ITEM_H + rows.filter((r) => r.hasDivider).length * DIVIDER_H + 6;
|
||||
const x =
|
||||
req.x + MENU_W > window.innerWidth - EDGE_GAP
|
||||
? Math.max(EDGE_GAP, req.x - MENU_W)
|
||||
: req.x;
|
||||
req.x + MENU_W > window.innerWidth - EDGE_GAP ? Math.max(EDGE_GAP, req.x - MENU_W) : req.x;
|
||||
const y =
|
||||
req.y + height > window.innerHeight - EDGE_GAP
|
||||
? Math.max(EDGE_GAP, req.y - height)
|
||||
: req.y;
|
||||
req.y + height > window.innerHeight - EDGE_GAP ? Math.max(EDGE_GAP, req.y - height) : req.y;
|
||||
return { x, y };
|
||||
});
|
||||
|
||||
@@ -108,11 +120,13 @@ export class ContextMenu {
|
||||
close(): void {
|
||||
if (!this.menu.request()) return;
|
||||
this.menu.close();
|
||||
this.activeIndex.set(-1); // a fresh menu starts with nothing highlighted
|
||||
this.focus.bump();
|
||||
}
|
||||
|
||||
pick(item: MenuItem): void {
|
||||
pick(item: MenuItem, event?: Event): void {
|
||||
// labels: don't let the click also toggle the real checkbox — the fresh
|
||||
// `checked` state comes back through settings when the menu reopens
|
||||
event?.preventDefault();
|
||||
if (!item.action) return;
|
||||
const action = item.action;
|
||||
this.close();
|
||||
@@ -126,7 +140,6 @@ export class ContextMenu {
|
||||
}
|
||||
|
||||
onKeydown(event: KeyboardEvent): void {
|
||||
const items = this.menu.request()?.items ?? [];
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
@@ -135,33 +148,38 @@ export class ContextMenu {
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
this.step(items, 1);
|
||||
this.step(1);
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
this.step(items, -1);
|
||||
this.step(-1);
|
||||
break;
|
||||
case 'Enter':
|
||||
case ' ': {
|
||||
// buttons activate natively from their own focus; labels don't, so
|
||||
// synthesize the click (which routes through pick via the template)
|
||||
const active = document.activeElement;
|
||||
if (active instanceof HTMLLabelElement && this.menuEl()?.nativeElement.contains(active)) {
|
||||
event.preventDefault();
|
||||
const item = items[this.activeIndex()];
|
||||
if (item) this.pick(item);
|
||||
active.click();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Move the highlight by one selectable item, skipping separators, wrapping. */
|
||||
private step(items: MenuItem[], delta: number): void {
|
||||
const count = items.length;
|
||||
if (!count) return;
|
||||
let index = this.activeIndex();
|
||||
for (let hops = 0; hops < count; hops++) {
|
||||
index = (index + delta + count) % count;
|
||||
if (items[index].type !== 'separator') {
|
||||
this.activeIndex.set(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Move REAL focus by one item, wrapping. 7.css styles :focus-visible the
|
||||
* same as :hover, and keyboard-initiated programmatic focus counts as
|
||||
* focus-visible, so the highlight comes from the framework for free.
|
||||
*/
|
||||
private step(delta: number): void {
|
||||
const root = this.menuEl()?.nativeElement;
|
||||
if (!root) return;
|
||||
const targets = Array.from(root.querySelectorAll<HTMLElement>('li > button, li > label'));
|
||||
if (!targets.length) return;
|
||||
const current = targets.indexOf(document.activeElement as HTMLElement);
|
||||
const next = current < 0 && delta < 0 ? targets.length - 1 : (current + delta + targets.length) % targets.length;
|
||||
targets[next].focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="window glass flex h-screen flex-col" [class.active]="windowFocused()">
|
||||
<div class="window glass w7-frame flex h-screen flex-col" [class.active]="windowFocused()">
|
||||
@if (!ctx) {
|
||||
<div class="flex flex-1 items-center justify-center">
|
||||
<div class="max-w-sm rounded-lg border border-border bg-card p-6 text-center shadow-sm">
|
||||
|
||||
+30
-49
@@ -580,18 +580,6 @@ body {
|
||||
content: none;
|
||||
}
|
||||
|
||||
/* Context-menu rows are <button>s, so 7.css would paint each one as an Aero
|
||||
push-button slab. Strip the slab here (revert-layer needs `skin`); the
|
||||
Win7 menu paint itself is stated in `skinfix` below. */
|
||||
.win7 .w7-menu button {
|
||||
background: revert-layer;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.win7 .w7-menu button::before,
|
||||
.win7 .w7-menu button::after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Above `skin`, for rules that must state a value rather than defer to a
|
||||
@@ -647,8 +635,11 @@ body {
|
||||
offset+blur+spread = 13px down/right. An undersized gutter hard-clips a
|
||||
70%-black shadow into a solid dark band around the window ("way too
|
||||
strong"); matched to the extents it falls off to nothing. h-screen (100vh)
|
||||
shrinks by the same totals or the gutter produces a scrollbar. */
|
||||
.win7 .window {
|
||||
shrinks by the same totals or the gutter produces a scrollbar.
|
||||
Keyed on `w7-frame` — the app hook the three window ROOTS carry — never on
|
||||
bare .window: this sizing is "the window that IS the page", and on any
|
||||
other .window (dialogs, demos) 100vh is nonsense. */
|
||||
.win7 .window.w7-frame {
|
||||
margin: 9px 13px 13px 9px;
|
||||
height: calc(100vh - 22px);
|
||||
}
|
||||
@@ -704,43 +695,33 @@ body {
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* Aero context menu. 7.css has no menu component, so this is hand-authored —
|
||||
but ONLY out of the existing --w7-* vars, never literal hex, so the
|
||||
hand-written dark Win7 palette above recolours it for free. Values are
|
||||
stated (not reverted), hence `skinfix` rather than `skin`.
|
||||
A real Win7 menu is a square-cornered white slab with a hairline border
|
||||
and a gradient selection band that spans the full row. */
|
||||
.win7 .w7-menu {
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--w7-el-bd);
|
||||
background: var(--w7-surface);
|
||||
/* ---- Menu (context menu) ----
|
||||
The menu itself is pure 7.css — ul[role=menu] > li[role=menuitem], see
|
||||
ui/src/app/context-menu.ts for the exact contract. (A previous iteration
|
||||
claimed "7.css has no menu component" and hand-built a lookalike here;
|
||||
false — the markup was one level off and its selectors never matched.)
|
||||
Only menu.css's hardcoded LIGHT literals need restating for the dark
|
||||
palette, same as the ListView above: the black `color:initial`, the
|
||||
white bevel halves of the gutter stripe and dividers, and the checked-box
|
||||
ring (#b3d3f9). */
|
||||
.win7[data-theme='dark'] ul[role='menu'] {
|
||||
color: var(--w7-el-c);
|
||||
font: var(--w7-font);
|
||||
padding: 2px;
|
||||
box-shadow: 0 2px 6px #0000004d;
|
||||
border-color: var(--w7-el-bd-d);
|
||||
}
|
||||
.win7 .w7-menu button {
|
||||
border-radius: 0;
|
||||
padding: 3px 8px;
|
||||
.win7[data-theme='dark'] ul[role='menu']:before {
|
||||
box-shadow:
|
||||
inset 1px 0 #00000059,
|
||||
inset -1px 0 #ffffff14;
|
||||
}
|
||||
.win7[data-theme='dark'] ul [role='menuitem'].has-divider:after {
|
||||
box-shadow:
|
||||
inset 0 1px #00000059,
|
||||
inset 0 -1px #ffffff14;
|
||||
}
|
||||
.win7[data-theme='dark'] ul [role='menuitem'] > input[type] + label:before {
|
||||
box-shadow: 0 0 0 1px var(--w7-li-bd-hl);
|
||||
}
|
||||
.win7[data-theme='dark'] ul [role='menuitem'] > input[type='checkbox']:checked + label:after {
|
||||
color: var(--w7-el-c);
|
||||
}
|
||||
/* Hover AND keyboard highlight share the band — the utility `bg-accent` that
|
||||
drives the keyboard cursor is overridden here, so both look identical. */
|
||||
.win7 .w7-menu button:hover,
|
||||
.win7 .w7-menu button.bg-accent {
|
||||
background: var(--w7-li-bg-hl);
|
||||
border: 1px solid var(--w7-li-bd-hl);
|
||||
/* keep the 1px border from shifting the label */
|
||||
padding: 2px 7px;
|
||||
}
|
||||
.win7 .w7-menu .w7-menu-sep {
|
||||
border-color: var(--w7-el-bd);
|
||||
margin: 2px 0;
|
||||
}
|
||||
/* Accelerator hints ride the muted colour, like Win7's grey shortcut column.
|
||||
Keyed on the utility class the accelerator span carries — `:last-child`
|
||||
would also catch the LABEL on every item that has no accelerator. */
|
||||
.win7 .w7-menu button > span.text-muted-foreground {
|
||||
color: var(--w7-el-c-d);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user