Stage 3: split styles.css into a shared base plus per-skin sheets
The 919-line stylesheet had a clean seam: `main`'s version is 278 lines and this branch only ever APPENDED to it. Verified that directly — a diff of main's styles.css against our first 298 lines differs only by the @layer statement and the 7.css import. - ui/src/styles.css (296) keeps the shared half: the @layer statement, the Tailwind import, the OKLCH tokens, @theme inline, the data-text interface sizes, the animations and the accent axis. The @layer statement must stay first here — it is one of only two rule types allowed above @import, and it is what sorts `skin` above `utilities`. - app/skins/7like/7like.css (636) takes the whole win7 section plus the scoped-7.css import that feeds layer(skin). - app/skins/shadlike/shadlike.css is near-empty by nature, not by omission: that skin IS the shared token system plus inline Tailwind, so there is no framework to bridge and no cascade to invert. The file documents why, and what does/does not belong in it. Both are @imported from styles.css, so one bundle still ships both, scoped by `.win7` exactly as before. One real trap caught here: the @font-face url() is resolved relative to the stylesheet, so moving the win7 section three directories deeper would have silently 404'd Noto Sans — the build stays green and the app just falls back to a different font. Fixed by moving the fonts INTO the skin (they exist only for the Win7 look, where Noto Sans substitutes for Segoe UI), which keeps `./fonts/...` correct wherever the folder goes. Confirmed in the built output: both woff2 emitted as hashed assets with the url rewritten to ./media/. Verified: build clean, built CSS keeps `@layer theme,base,components, utilities,skin,skinfix` and all 7.css selectors, the live window's opaque interior is pixel-identical to the pre-refactor baseline, and both harness renders (light and dark) diff to exactly zero. A layer-assignment mistake here would not be subtle — 7.css would lose to ~1900 Tailwind utilities and the app would look nothing like this. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,636 @@
|
|||||||
|
/* The SCOPED 7.css build: postcss-prefix-selector rewrote every selector to
|
||||||
|
`.win7` (root rules) or `.win7 <sel>` (the rest), so the whole framework is
|
||||||
|
inert until SettingsService.applySkin puts that class on <html>. All 61
|
||||||
|
icons are data URIs, so nothing needs copying into assets — and NOT the
|
||||||
|
`7.inline.css` build, which flattens away the --w7-* vars the dark palette
|
||||||
|
below depends on. The deep path is required: 7.css ships no `exports` map
|
||||||
|
and Tailwind's resolver only reads `style`/`mainFields`, so a bare
|
||||||
|
`@import "7.css"` finds no entry. */
|
||||||
|
@import "7.css/dist/7.scoped.css" layer(skin);
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Windows 7 skin — SettingsService.applySkin puts `.win7` on <html>
|
||||||
|
==========================================================================
|
||||||
|
|
||||||
|
Three tiers, and the split matters:
|
||||||
|
|
||||||
|
- `@layer skin` (the 7.css import at the top of this file) owns the
|
||||||
|
CONTROLS: buttons, inputs, tabs, progress bars, tree views, the Aero
|
||||||
|
title bar. It sits ABOVE `utilities`, so a bare `button` rule in 7.css
|
||||||
|
beats `.rounded-md` on that same button. That inversion is the whole
|
||||||
|
trick — it restyles ~1900 inline utility classes with no template edits.
|
||||||
|
- This unlayered section owns the BRIDGE: it re-points the app's own
|
||||||
|
semantic tokens at Win7 values, so utilities Tailwind still controls
|
||||||
|
(bg-card, border-border, text-muted-foreground) also come out
|
||||||
|
Win7-correct. It adds NO new tokens on purpose — a new colour token
|
||||||
|
would need a matching `--color-*` line in `@theme inline`.
|
||||||
|
- `@layer skinfix` (last, above `skin`) hands individual properties BACK to
|
||||||
|
the utilities layer with `revert-layer`, for the places where 7.css's
|
||||||
|
Win7-dialog metrics are wrong for this app.
|
||||||
|
|
||||||
|
WARNING for future edits: because `skin` outranks `utilities`, any Tailwind
|
||||||
|
utility added later to a <button>, <input>, <select>, <textarea> or <table>
|
||||||
|
silently loses under this skin. `skinfix` is the only place to fix that.
|
||||||
|
|
||||||
|
Specificity notes — the blocks below must beat the token blocks above:
|
||||||
|
:root.win7 (0,2,0) beats base :root (0,1,0)
|
||||||
|
:root.win7[data-theme='dark'] (0,3,0) beats :root[data-theme=…] (0,2,0)
|
||||||
|
and ties the darker-tone block (0,3,0),
|
||||||
|
winning on source order (it is last).
|
||||||
|
The accent formula blocks are deliberately NOT overridden: they only set
|
||||||
|
--primary/--ring/--cursor-tint, and letting them through is what makes the
|
||||||
|
accent picker recolour the Aero chrome — authentic, Win7 shipped exactly
|
||||||
|
that ("Window Color" personalization). */
|
||||||
|
|
||||||
|
/* Segoe UI is not on macOS/Linux and is not redistributable. Noto Sans is the
|
||||||
|
substitute 7.css itself names in its --w7-font fallback stack. Vendored
|
||||||
|
(OFL-1.1) from @fontsource/noto-sans@5.3.0, files/noto-sans-latin-{400,700}
|
||||||
|
-normal.woff2 -> ./fonts/, i.e. INSIDE this skin: the font exists only for
|
||||||
|
the Win7 look, and keeping it here means the url() below stays relative to
|
||||||
|
this file wherever the skin folder moves. (It used to sit at ui/src/fonts/
|
||||||
|
next to the old single stylesheet — splitting this file out rebased the
|
||||||
|
url() three directories deeper and would have silently 404'd the font.)
|
||||||
|
Vendored rather than @imported from node_modules because Tailwind inlines
|
||||||
|
imported CSS, which would leave @fontsource's relative url() rebased against
|
||||||
|
the wrong directory. They live under src/ (not public/) so esbuild emits them
|
||||||
|
as hashed assets and rewrites the url() — a public/ path is not resolvable at
|
||||||
|
build time. */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Noto Sans App';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: block;
|
||||||
|
src: url('./fonts/noto-sans-latin-400-normal.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Noto Sans App';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: block;
|
||||||
|
src: url('./fonts/noto-sans-latin-700-normal.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.win7 {
|
||||||
|
/* Real Segoe first (Windows), bundled substitute everywhere else. */
|
||||||
|
--w7-family: 'Segoe UI', 'SegoeUI', 'Noto Sans App', system-ui, sans-serif;
|
||||||
|
/* ~7.css's 9pt, but in px so it tracks the interface-size axis below.
|
||||||
|
Restating the `font` SHORTHAND (not just the family) is deliberate:
|
||||||
|
7.css applies `font: var(--w7-font)` on 12 component rules, and the
|
||||||
|
shorthand also resets weight and line-height — pinning 400/1.4 here stops
|
||||||
|
those rules from flattening the app's `font-medium` / leading. */
|
||||||
|
--w7-fs: 12px;
|
||||||
|
--w7-font: 400 var(--w7-fs) / 1.4 var(--w7-family);
|
||||||
|
|
||||||
|
/* Surface ramp, Win7 Explorer. This INVERTS the modern ramp's direction:
|
||||||
|
there --chrome (frame strips) sits below --background (content panes);
|
||||||
|
here the content pane is the brightest thing on screen and the frame is
|
||||||
|
the #f0f0f0 window surface. Deliberate — that is what Explorer was. */
|
||||||
|
--background: #ffffff;
|
||||||
|
--chrome: #f0f0f0;
|
||||||
|
--card: #f0f0f0;
|
||||||
|
--foreground: #000000;
|
||||||
|
--card-foreground: #000000;
|
||||||
|
--muted-foreground: #6d6d6d;
|
||||||
|
--secondary: #e8e8e8;
|
||||||
|
--secondary-foreground: #000000;
|
||||||
|
--muted: #e8e8e8;
|
||||||
|
/* Win7's hover is a pale blue wash, not a grey — matches --w7-el-grad-h */
|
||||||
|
--accent: #eaf6fd;
|
||||||
|
--accent-foreground: #000000;
|
||||||
|
--border: #d5d5d5;
|
||||||
|
--input: #abadb3; /* 7.css's own text-input border */
|
||||||
|
--destructive: #c62828;
|
||||||
|
--destructive-foreground: #ffffff;
|
||||||
|
--success: #06b025; /* the Win7 progress-bar green */
|
||||||
|
|
||||||
|
/* 5px, NOT 3px: `rounded-md` is 74 of the 112 radius utilities in the
|
||||||
|
templates and compiles to calc(var(--radius) - 2px), so 5 lands it exactly
|
||||||
|
on --w7-el-bdr (3px). --radius itself feeds `rounded-lg` (12 uses) at 5px,
|
||||||
|
near the 6px window radius. Bare `rounded` (26 uses) is a Tailwind literal
|
||||||
|
0.25rem and is unaffected. */
|
||||||
|
--radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interface size: Win7 shipped this exact axis ("Smaller / Medium / Larger"),
|
||||||
|
so scaling is more authentic than disabling it. One indirection covers all
|
||||||
|
three tiers because var substitution is lazy. The --row-h / --list-font
|
||||||
|
block further up scales the file list independently and is untouched. */
|
||||||
|
:root.win7[data-text='compact'] { --w7-fs: 11px; }
|
||||||
|
:root.win7[data-text='large'] { --w7-fs: 13px; }
|
||||||
|
|
||||||
|
/* Light values for every 7.css literal the skinfix rules below restate through
|
||||||
|
vars (7.css hardcodes these outside its var system). The values are 7.css's
|
||||||
|
own, verbatim — light must render exactly as stock. Dark overrides live in
|
||||||
|
the duplicated dark blocks above, which is the house pattern: rules are
|
||||||
|
written ONCE and theme through tokens. (The previous attribute-keyed dark
|
||||||
|
rules silently missed SYSTEM-dark — the media-query path sets no attribute.) */
|
||||||
|
:root.win7 {
|
||||||
|
/* ListView (listview.css) */
|
||||||
|
--w7-lv-bg: #fff;
|
||||||
|
--w7-lv-bd: #c0c1cd;
|
||||||
|
--w7-lv-c: #000;
|
||||||
|
--w7-lv-head-grad: linear-gradient(180deg, #fff 45%, #fafafa 0, #f0f0f0);
|
||||||
|
--w7-lv-head-bd: #d7d7d7;
|
||||||
|
--w7-lv-cell-bd: #eee;
|
||||||
|
/* Tabs (tabs.css). 7.css hardcodes `color: #222` on every tablist button,
|
||||||
|
outside its own var system, which is unreadable on the dark surface. */
|
||||||
|
--w7-tab-c: #222;
|
||||||
|
/* Menu (menu.css) */
|
||||||
|
--w7-mn-c: #000;
|
||||||
|
--w7-mn-bd: #0006;
|
||||||
|
--w7-mn-stripe: inset 1px 0 #00000026, inset -1px 0 #fff;
|
||||||
|
--w7-mn-divider: inset 0 1px #00000026, inset 0 -1px #fff;
|
||||||
|
--w7-mn-check-ring: #b3d3f9;
|
||||||
|
--w7-mn-check-c: #0c12a1;
|
||||||
|
/* Glass caption streaks (window.css .window.glass>.title-bar): a literal.
|
||||||
|
The two #fff5 corner shines wash a DARK caption silver, so dark subdues
|
||||||
|
every white in the stack while keeping the streak structure. */
|
||||||
|
--w7-glass-title: linear-gradient(135deg, #fff5 70px, transparent 100px), linear-gradient(225deg, #fff5 70px, transparent 100px), linear-gradient(54deg, #0002 0 4%, #6661 6% 6%, #0002 8% 10%, #0002 15% 16%, #aaa1 17% 18%, #0002 23% 24%, #bbb2 25% 26%, #0002 31% 33%, #0002 34% 34.5%, #bbb2 36% 40%, #0002 41% 41.5%, #bbb2 44% 45%, #bbb2 46% 47%, #0002 48% 49%, #0002 50% 50.5%, #0002 56% 56.5%, #bbb2 57% 63%, #0002 67% 69%, #bbb2 69.5% 70%, #0002 73.5% 74%, #bbb2 74.5% 79%, #0002 80% 84%, #aaa2 85% 86%, #0002 87%, #bbb1 90%) 0 / 100vw 100vh no-repeat fixed;
|
||||||
|
/* Caption text (window.css .title-bar-text: black + 8-fold white halo) */
|
||||||
|
--w7-cap-c: #000;
|
||||||
|
--w7-cap-glow: 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff,
|
||||||
|
0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hand-authored dark Win7. Microsoft never shipped one — this exists so the
|
||||||
|
theme and tone settings stay live under the skin. Only the colour-carrying
|
||||||
|
--w7-* vars are listed: --w7-el-grad and --w7-w-grad are DERIVED from the
|
||||||
|
values below and recolour for free, while --w7-el-grad-h/-a and
|
||||||
|
--w7-li-bg-hl are literal hex in 7.css and so must be restated. The ~25
|
||||||
|
geometry vars and ~20 base64 icon vars are theme-independent.
|
||||||
|
DUPLICATED for forced-dark and system-dark, per the rule the token blocks
|
||||||
|
above set: every dark value goes in BOTH. */
|
||||||
|
:root.win7[data-theme='dark'] {
|
||||||
|
--w7-surface: #2b2b2b;
|
||||||
|
--w7-el-bg: #3a3a3a;
|
||||||
|
--w7-el-bg-d: #333333;
|
||||||
|
--w7-el-bg-s-1: #333333;
|
||||||
|
--w7-el-bg-s-2: #262626;
|
||||||
|
--w7-el-bd: #565656;
|
||||||
|
--w7-el-bd-h: #4a8cbe;
|
||||||
|
--w7-el-bd-a: #3f6c8c;
|
||||||
|
--w7-el-bd-d: #3f3f3f;
|
||||||
|
--w7-el-c: #f0f0f0;
|
||||||
|
--w7-el-c-d: #6f6f6f;
|
||||||
|
--w7-el-sd: inset 0 0 0 1px #ffffff14;
|
||||||
|
--w7-el-grad-h: linear-gradient(#3c5a6b 45%, #315062 45%, #2b4557);
|
||||||
|
--w7-el-grad-a: linear-gradient(#2c4657, #24384a 30% 50%, #1d2f3f 50%, #16242f);
|
||||||
|
--w7-li-bd-hl: #3f6c8c;
|
||||||
|
--w7-li-bg-hl: linear-gradient(#ffffff14, #4a6b8433 90%, #ffffff1a);
|
||||||
|
--w7-w-bd: #000000e6;
|
||||||
|
/* Ares-style black glass: near-black ground; 7.css's own alpha streaks and
|
||||||
|
highlight gradients become the gloss. Blue lives in accents (selection,
|
||||||
|
active tab), not in the glass itself. */
|
||||||
|
--w7-w-bg: #101013;
|
||||||
|
--w7-tab-bg: #1c1c1e;
|
||||||
|
--w7-title-c: #f5f5f5;
|
||||||
|
--w7-link-c: #6fb3ff;
|
||||||
|
/* literal restatements consumed by the skinfix rules (see :root.win7) */
|
||||||
|
--w7-lv-bg: var(--w7-el-bg-s-2);
|
||||||
|
--w7-lv-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-lv-c: var(--w7-el-c);
|
||||||
|
--w7-lv-head-grad: linear-gradient(180deg, var(--w7-el-bg) 45%, var(--w7-el-bg-d) 0, var(--w7-el-bg-s-1));
|
||||||
|
--w7-lv-head-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-lv-cell-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-tab-c: var(--w7-el-c);
|
||||||
|
--w7-mn-c: var(--w7-el-c);
|
||||||
|
--w7-mn-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-mn-stripe: inset 1px 0 #00000059, inset -1px 0 #ffffff14;
|
||||||
|
--w7-mn-divider: inset 0 1px #00000059, inset 0 -1px #ffffff14;
|
||||||
|
--w7-mn-check-ring: var(--w7-li-bd-hl);
|
||||||
|
--w7-mn-check-c: var(--w7-el-c);
|
||||||
|
--w7-glass-title: linear-gradient(135deg, #ffffff1a 70px, transparent 100px), linear-gradient(225deg, #ffffff1a 70px, transparent 100px), linear-gradient(54deg, #0003 0 4%, #ffffff08 6% 6%, #0003 8% 10%, #0003 15% 16%, #ffffff0d 17% 18%, #0003 23% 24%, #ffffff10 25% 26%, #0003 31% 33%, #0003 34% 34.5%, #ffffff10 36% 40%, #0003 41% 41.5%, #ffffff10 44% 45%, #ffffff10 46% 47%, #0003 48% 49%, #0003 50% 50.5%, #0003 56% 56.5%, #ffffff10 57% 63%, #0003 67% 69%, #ffffff10 69.5% 70%, #0003 73.5% 74%, #ffffff10 74.5% 79%, #0003 80% 84%, #ffffff0d 85% 86%, #0003 87%, #ffffff0a 90%) 0 / 100vw 100vh no-repeat fixed;
|
||||||
|
/* Ares: white caption on black glass, dark halo instead of white */
|
||||||
|
--w7-cap-c: var(--w7-title-c);
|
||||||
|
--w7-cap-glow: 0 0 8px #000c, 0 0 8px #000c, 0 1px 2px #000;
|
||||||
|
--w7-link-c-h: #9acbff;
|
||||||
|
|
||||||
|
--background: #1f1f1f;
|
||||||
|
--chrome: #2b2b2b;
|
||||||
|
--card: #2b2b2b;
|
||||||
|
--foreground: #f0f0f0;
|
||||||
|
--card-foreground: #f0f0f0;
|
||||||
|
--muted-foreground: #a0a0a0;
|
||||||
|
--secondary: #383838;
|
||||||
|
--secondary-foreground: #f0f0f0;
|
||||||
|
--muted: #383838;
|
||||||
|
--accent: #2f4a5c;
|
||||||
|
--accent-foreground: #f0f0f0;
|
||||||
|
--border: #3c3c3c;
|
||||||
|
--input: #55585c;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root.win7:not([data-theme='light']) {
|
||||||
|
--w7-surface: #2b2b2b;
|
||||||
|
--w7-el-bg: #3a3a3a;
|
||||||
|
--w7-el-bg-d: #333333;
|
||||||
|
--w7-el-bg-s-1: #333333;
|
||||||
|
--w7-el-bg-s-2: #262626;
|
||||||
|
--w7-el-bd: #565656;
|
||||||
|
--w7-el-bd-h: #4a8cbe;
|
||||||
|
--w7-el-bd-a: #3f6c8c;
|
||||||
|
--w7-el-bd-d: #3f3f3f;
|
||||||
|
--w7-el-c: #f0f0f0;
|
||||||
|
--w7-el-c-d: #6f6f6f;
|
||||||
|
--w7-el-sd: inset 0 0 0 1px #ffffff14;
|
||||||
|
--w7-el-grad-h: linear-gradient(#3c5a6b 45%, #315062 45%, #2b4557);
|
||||||
|
--w7-el-grad-a: linear-gradient(#2c4657, #24384a 30% 50%, #1d2f3f 50%, #16242f);
|
||||||
|
--w7-li-bd-hl: #3f6c8c;
|
||||||
|
--w7-li-bg-hl: linear-gradient(#ffffff14, #4a6b8433 90%, #ffffff1a);
|
||||||
|
--w7-w-bd: #000000e6;
|
||||||
|
--w7-w-bg: #101013;
|
||||||
|
--w7-tab-bg: #1c1c1e;
|
||||||
|
--w7-title-c: #f5f5f5;
|
||||||
|
--w7-link-c: #6fb3ff;
|
||||||
|
/* literal restatements consumed by the skinfix rules (see :root.win7) */
|
||||||
|
--w7-lv-bg: var(--w7-el-bg-s-2);
|
||||||
|
--w7-lv-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-lv-c: var(--w7-el-c);
|
||||||
|
--w7-lv-head-grad: linear-gradient(180deg, var(--w7-el-bg) 45%, var(--w7-el-bg-d) 0, var(--w7-el-bg-s-1));
|
||||||
|
--w7-lv-head-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-lv-cell-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-tab-c: var(--w7-el-c);
|
||||||
|
--w7-mn-c: var(--w7-el-c);
|
||||||
|
--w7-mn-bd: var(--w7-el-bd-d);
|
||||||
|
--w7-mn-stripe: inset 1px 0 #00000059, inset -1px 0 #ffffff14;
|
||||||
|
--w7-mn-divider: inset 0 1px #00000059, inset 0 -1px #ffffff14;
|
||||||
|
--w7-mn-check-ring: var(--w7-li-bd-hl);
|
||||||
|
--w7-mn-check-c: var(--w7-el-c);
|
||||||
|
--w7-glass-title: linear-gradient(135deg, #ffffff1a 70px, transparent 100px), linear-gradient(225deg, #ffffff1a 70px, transparent 100px), linear-gradient(54deg, #0003 0 4%, #ffffff08 6% 6%, #0003 8% 10%, #0003 15% 16%, #ffffff0d 17% 18%, #0003 23% 24%, #ffffff10 25% 26%, #0003 31% 33%, #0003 34% 34.5%, #ffffff10 36% 40%, #0003 41% 41.5%, #ffffff10 44% 45%, #ffffff10 46% 47%, #0003 48% 49%, #0003 50% 50.5%, #0003 56% 56.5%, #ffffff10 57% 63%, #0003 67% 69%, #ffffff10 69.5% 70%, #0003 73.5% 74%, #ffffff10 74.5% 79%, #0003 80% 84%, #ffffff0d 85% 86%, #0003 87%, #ffffff0a 90%) 0 / 100vw 100vh no-repeat fixed;
|
||||||
|
/* Ares: white caption on black glass, dark halo instead of white */
|
||||||
|
--w7-cap-c: var(--w7-title-c);
|
||||||
|
--w7-cap-glow: 0 0 8px #000c, 0 0 8px #000c, 0 1px 2px #000;
|
||||||
|
--w7-link-c-h: #9acbff;
|
||||||
|
|
||||||
|
--background: #1f1f1f;
|
||||||
|
--chrome: #2b2b2b;
|
||||||
|
--card: #2b2b2b;
|
||||||
|
--foreground: #f0f0f0;
|
||||||
|
--card-foreground: #f0f0f0;
|
||||||
|
--muted-foreground: #a0a0a0;
|
||||||
|
--secondary: #383838;
|
||||||
|
--secondary-foreground: #f0f0f0;
|
||||||
|
--muted: #383838;
|
||||||
|
--accent: #2f4a5c;
|
||||||
|
--accent-foreground: #f0f0f0;
|
||||||
|
--border: #3c3c3c;
|
||||||
|
--input: #55585c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark tone "darker": Aero has no meaningful true black, so rather than leave
|
||||||
|
the Tone row a dead control the skin just goes a step deeper. Duplicated
|
||||||
|
forced + system like everything else. */
|
||||||
|
:root.win7[data-theme='dark'][data-dark-tone='darker'] {
|
||||||
|
--w7-surface: #1a1a1a;
|
||||||
|
--w7-el-bg: #262626;
|
||||||
|
--w7-w-bg: #060607;
|
||||||
|
--background: #121212;
|
||||||
|
--chrome: #1a1a1a;
|
||||||
|
--card: #1a1a1a;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root.win7:not([data-theme='light'])[data-dark-tone='darker'] {
|
||||||
|
--w7-surface: #1a1a1a;
|
||||||
|
--w7-el-bg: #262626;
|
||||||
|
--w7-w-bg: #060607;
|
||||||
|
--background: #121212;
|
||||||
|
--chrome: #1a1a1a;
|
||||||
|
--card: #1a1a1a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accent -> Aero window colour, so the existing accent picker keeps meaning
|
||||||
|
under the skin. --w7-w-bg is the one colour 7.css documents as settable.
|
||||||
|
LIGHT ONLY: in dark the glass is Ares black regardless of accent (the accent
|
||||||
|
keeps tinting selection etc. through --primary). These arms out-rank the
|
||||||
|
accent rule below on specificity — without them it wins the tie on source
|
||||||
|
order and dark glass turns accent-coloured. */
|
||||||
|
:root.win7[data-accent] {
|
||||||
|
--w7-w-bg: var(--primary);
|
||||||
|
}
|
||||||
|
:root.win7[data-theme='dark'][data-accent] {
|
||||||
|
--w7-w-bg: #101013;
|
||||||
|
}
|
||||||
|
:root.win7[data-theme='dark'][data-dark-tone='darker'][data-accent] {
|
||||||
|
--w7-w-bg: #060607;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root.win7:not([data-theme='light'])[data-accent] {
|
||||||
|
--w7-w-bg: #101013;
|
||||||
|
}
|
||||||
|
:root.win7:not([data-theme='light'])[data-dark-tone='darker'][data-accent] {
|
||||||
|
--w7-w-bg: #060607;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlayered on purpose: the html/body rule above sets font-family unlayered,
|
||||||
|
so a layered override would lose. Family only — NOT font-size: the app's
|
||||||
|
rem-based utilities are root-relative, and shifting the root would move
|
||||||
|
every line box for no gain. */
|
||||||
|
.win7 body {
|
||||||
|
font-family: var(--w7-family);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The win7 window is a FRAMELESS, natively transparent window whose only
|
||||||
|
opaque surface is meant to be `.window` — that is the whole premise of the
|
||||||
|
6px rounded frame, since the area outside the radius must let the desktop
|
||||||
|
through. The unlayered `html, body { background: var(--background) }` above
|
||||||
|
broke that premise silently: it painted an opaque rectangle behind the
|
||||||
|
frame, so the rounded corners had an opaque ground filling them in and the
|
||||||
|
window read as a rectangle with a drawn-on curve.
|
||||||
|
Unlayered on purpose, same reason as the font-family rule: the html/body
|
||||||
|
background it overrides is itself unlayered, and unlayered declarations beat
|
||||||
|
every @layer regardless of specificity — a `skinfix` rule could not touch
|
||||||
|
it. Scoped to .win7 so the modern skin keeps its opaque ground. */
|
||||||
|
html.win7,
|
||||||
|
html.win7 body {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Neutralizers live in `skin`, NOT `skinfix`, and this is load-bearing:
|
||||||
|
`revert-layer` rolls a property back to the previous LAYER, so from inside
|
||||||
|
`skin` it lands on `utilities` (the Tailwind classes already in the
|
||||||
|
templates) — which is the whole point. From `skinfix` it would land back on
|
||||||
|
7.css and do nothing. Within `skin` these beat the imported rules on equal
|
||||||
|
specificity by source order, since the import is at the top of this file. */
|
||||||
|
@layer skin {
|
||||||
|
/* 7.css sizes every <button> like a Win7 dialog push button
|
||||||
|
(min-width 75px, min-height 23px, padding 0 12px). Most of this app's 84
|
||||||
|
buttons are ~24px icon squares sized by utilities, so without this the tab
|
||||||
|
bar wraps and the options popover overflows. Hand the BOX MODEL back to
|
||||||
|
the utilities layer and keep 7.css's paint. The title bar's own controls
|
||||||
|
are matched by a MORE specific 7.css selector (.title-bar-controls button)
|
||||||
|
and so keep their Aero glyph metrics untouched. */
|
||||||
|
.win7 button {
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
padding: revert-layer;
|
||||||
|
text-align: revert-layer;
|
||||||
|
color: revert-layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7.css hardcodes #fff / #abadb3 / #222 on form controls and tables, outside
|
||||||
|
its own var system. Reverting routes them back through the bridged tokens
|
||||||
|
above, which is what makes dark mode nearly free. */
|
||||||
|
.win7 input[type='text'],
|
||||||
|
.win7 input[type='password'],
|
||||||
|
.win7 input[type='number'],
|
||||||
|
.win7 input[type='search'],
|
||||||
|
.win7 textarea {
|
||||||
|
background-color: revert-layer;
|
||||||
|
border-color: revert-layer;
|
||||||
|
}
|
||||||
|
.win7 table {
|
||||||
|
background-color: revert-layer;
|
||||||
|
color: revert-layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Win7 toolbar buttons are FLAT with a hover glow, not Aero slabs —
|
||||||
|
Explorer's command bar worked exactly that way. `w7-toolbar` marks the
|
||||||
|
three header rows; a plain `.title-bar` sibling is deliberately NOT one of
|
||||||
|
them, so its controls keep the real Aero treatment. `w7-flat` is the
|
||||||
|
per-button escape hatch. Both classes are inert under the modern skin.
|
||||||
|
`:not([role='tab'])` is load-bearing: the workspace strip IS a w7-toolbar,
|
||||||
|
so without the exemption this rule would strip the border, gradient and
|
||||||
|
both pseudo-elements off every tab and leave 7.css's tab component drawing
|
||||||
|
nothing. Tabs want the full Aero slab; the +/Connections/options buttons
|
||||||
|
beside them want the flat treatment.
|
||||||
|
Only `background` stays here — it is the one property that needs to land
|
||||||
|
on `utilities` via revert-layer. The literals that suppress the Aero slab
|
||||||
|
moved to `skinfix`, because 7.css restates border-color at
|
||||||
|
`.win7 button:not(:disabled):not(:hover)` = (0,3,1), which outranks
|
||||||
|
`.win7 .w7-flat` = (0,2,0). The toolbar arm only ever won that fight by
|
||||||
|
tying on specificity and landing later in the same layer — an accident,
|
||||||
|
and one w7-flat did not share: it rendered a bordered slab regardless. */
|
||||||
|
.win7 .w7-toolbar button:not([role='tab']),
|
||||||
|
.win7 .w7-flat {
|
||||||
|
background: revert-layer;
|
||||||
|
}
|
||||||
|
.win7 .w7-toolbar button:not([role='tab'])::before,
|
||||||
|
.win7 .w7-toolbar button:not([role='tab'])::after,
|
||||||
|
.win7 .w7-flat::before,
|
||||||
|
.win7 .w7-flat::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Above `skin`, for rules that must state a value rather than defer to a
|
||||||
|
neighbouring layer. */
|
||||||
|
@layer skinfix {
|
||||||
|
/* Opt back IN to real Win7 push-button metrics, for the dialog and options
|
||||||
|
buttons where they are right. */
|
||||||
|
.win7 .w7-btn {
|
||||||
|
min-width: 75px;
|
||||||
|
min-height: 23px;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- ListView (file panels) ----
|
||||||
|
7.css's listview.css hardcodes light literals (#fff, #c0c1cd, #eee and the
|
||||||
|
white header gradient) instead of --w7-* vars, so unlike the rest of the
|
||||||
|
skin it does NOT follow the hand-authored dark palette — the header labels
|
||||||
|
came out invisible, dark on dark. These restate the same rules in dark
|
||||||
|
terms; light keeps 7.css's own values untouched.
|
||||||
|
|
||||||
|
CRITICAL: the base surface goes on `table`, NEVER on `tbody > tr > *`.
|
||||||
|
7.css paints the selection band on the ROW, and a cell background paints
|
||||||
|
over a row background — setting one erased the selection highlight
|
||||||
|
entirely in dark mode. */
|
||||||
|
.win7 table {
|
||||||
|
background-color: var(--w7-lv-bg);
|
||||||
|
border-color: var(--w7-lv-bd);
|
||||||
|
color: var(--w7-lv-c);
|
||||||
|
}
|
||||||
|
.win7 table > thead > tr > * {
|
||||||
|
background: var(--w7-lv-head-grad);
|
||||||
|
border-color: var(--w7-lv-head-bd);
|
||||||
|
color: var(--w7-lv-c);
|
||||||
|
}
|
||||||
|
.win7 table > tbody > tr > :not(:last-child) {
|
||||||
|
border-right-color: var(--w7-lv-cell-bd);
|
||||||
|
}
|
||||||
|
/* 7.css drops the cell separators inside a selected row; the rule above is
|
||||||
|
more specific than its, so restore that behaviour explicitly. */
|
||||||
|
.win7 table > tbody > tr.highlighted > :not(:last-child) {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
.win7 table > tbody > tr.highlighted {
|
||||||
|
background: var(--w7-li-bg-hl);
|
||||||
|
border-color: var(--w7-li-bd-hl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Window frame ----
|
||||||
|
The native window is transparent (chromeOptions in src/bun/windows.ts), and
|
||||||
|
nothing but .window may paint an opaque surface — that is what lets the 6px
|
||||||
|
corner radius actually be transparent at the corners. That invariant is NOT
|
||||||
|
free: html/body paint an opaque background unlayered, and had to be reset
|
||||||
|
to transparent for .win7 (see the rule above @layer skin). Anything else
|
||||||
|
that paints full-bleed will fill the corners the same way.
|
||||||
|
NO drop shadow, by request: 7.css's `2px 2px 10px 1px #000000b3` needed a
|
||||||
|
margin gutter to fall into and still read heavy against a real desktop, so
|
||||||
|
the frame keeps only the inset 1px glass highlight from that shorthand
|
||||||
|
(dropping the whole box-shadow would lose the highlight too — it lives in
|
||||||
|
the same declaration). No shadow -> no gutter -> full-bleed 100vh.
|
||||||
|
Keyed on `w7-frame` — the app hook the three window ROOTS carry — never on
|
||||||
|
bare .window: on any other .window (dialogs, demos) this sizing is
|
||||||
|
nonsense and 7.css's own shadow is fine. */
|
||||||
|
.win7 .window.w7-frame {
|
||||||
|
margin: 0;
|
||||||
|
height: 100vh;
|
||||||
|
box-shadow: inset 0 0 0 1px #fffa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A maximized Win7 window has SQUARE corners — the radius only exists to let
|
||||||
|
the desktop show past a floating frame, and a rounded maximized window
|
||||||
|
leaves four notches of desktop in the screen corners.
|
||||||
|
Read off the title bar's own `is-maximized` (title-bar.ts publishes it on
|
||||||
|
its host) with :has(), so the frame reacts without a second windowState
|
||||||
|
subscription. .title-bar is a DIRECT child of .window — app-title-bar
|
||||||
|
carries the class on its host element, which is why `>` holds here.
|
||||||
|
The radius is declared three times in 7.css — on .window and on both the
|
||||||
|
::before tint and the ::after glass layer — so all three must be zeroed or
|
||||||
|
a rounded pseudo-element still cuts the corner off the square frame. */
|
||||||
|
.win7 .window.w7-frame:has(> .title-bar.is-maximized),
|
||||||
|
.win7 .window.w7-frame:has(> .title-bar.is-maximized)::before,
|
||||||
|
.win7 .window.w7-frame:has(> .title-bar.is-maximized)::after {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7.css's glass layer is `backdrop-filter: blur(4px)` on .window::after —
|
||||||
|
but CSS cannot sample the DESKTOP behind a transparent native window
|
||||||
|
(there is no backdrop to read), and WebKitGTK paints the unsampleable
|
||||||
|
layer BLACK: every see-through part of the glass — the frame ring, the
|
||||||
|
area around the title-bar buttons — composited over black instead of over
|
||||||
|
the desktop. With it off, the real desktop shows through the glass tint,
|
||||||
|
which is closer to Aero than a blur we cannot have. (True blur-behind
|
||||||
|
needs compositor support; GNOME offers none to X11/XWayland clients.) */
|
||||||
|
.win7 .window.glass::after {
|
||||||
|
-webkit-backdrop-filter: none;
|
||||||
|
backdrop-filter: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Constant row geometry. 7.css gives ONLY tr.highlighted a 1px border, and
|
||||||
|
the table is border-collapse: collapse — so a selected row physically grew
|
||||||
|
1px at top and bottom and shoved every row below it down by 2px as the
|
||||||
|
cursor moved. Every row reserves that border instead, transparent when not
|
||||||
|
selected, so selecting changes colour and nothing else.
|
||||||
|
NOTE: this sits in `skinfix`, which outranks `skin` by LAYER regardless of
|
||||||
|
specificity, so it would also blank 7.css's highlight border — both
|
||||||
|
selected states therefore restate their border-color below. */
|
||||||
|
.win7 table > tbody > tr {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.win7 table > tbody > tr.highlighted {
|
||||||
|
border-color: var(--w7-li-bd-hl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pane-focus model (commander convention, matching Win7 Explorer):
|
||||||
|
the CURSOR ROW always carries the selection band in BOTH panels —
|
||||||
|
tr.highlighted covers "selected via Space" AND "cursor row" (the template
|
||||||
|
ORs them). What changes with pane focus is the band's strength, never
|
||||||
|
whether it exists:
|
||||||
|
- focused pane -> strong Aero band (below)
|
||||||
|
- unfocused pane -> 7.css's own tr.highlighted, which IS Win7's
|
||||||
|
inactive-selection wash — faint by design, not a bug.
|
||||||
|
The dotted focus rectangle is NOT the focus signal; it is layered on top
|
||||||
|
of the band in the focused pane only, the way Win7 drew the keyboard
|
||||||
|
focus rect over the selected item. */
|
||||||
|
.win7 table.is-active > tbody > tr.highlighted {
|
||||||
|
background: var(--w7-el-grad-h);
|
||||||
|
border-color: var(--w7-el-bd-h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* One dotted rectangle around the ROW; per cell (tr > *) draws a dotted box
|
||||||
|
around every cell instead — visibly wrong. */
|
||||||
|
.win7 table > tbody > tr.is-cursor {
|
||||||
|
outline: 1px dotted var(--w7-el-c);
|
||||||
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 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 ul[role='menu'] {
|
||||||
|
color: var(--w7-mn-c);
|
||||||
|
border-color: var(--w7-mn-bd);
|
||||||
|
}
|
||||||
|
.win7 ul[role='menu']:before {
|
||||||
|
box-shadow: var(--w7-mn-stripe);
|
||||||
|
}
|
||||||
|
.win7 ul [role='menuitem'].has-divider:after {
|
||||||
|
box-shadow: var(--w7-mn-divider);
|
||||||
|
}
|
||||||
|
.win7 ul [role='menuitem'] > input[type] + label:before {
|
||||||
|
box-shadow: 0 0 0 1px var(--w7-mn-check-ring);
|
||||||
|
}
|
||||||
|
.win7 ul [role='menuitem'] > input[type='checkbox']:checked + label:after {
|
||||||
|
color: var(--w7-mn-check-c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Flat buttons (toolbars + the w7-flat escape hatch) ----
|
||||||
|
The paint-suppression half of the rule in `skin` (see there for the split).
|
||||||
|
It lives in `skinfix` because 7.css restates border-color at (0,3,1) via
|
||||||
|
`.win7 button:not(:disabled):not(:hover)` and again on :hover, which no
|
||||||
|
reasonable `.w7-flat` selector outranks — layer order settles it instead
|
||||||
|
of a specificity arms race. */
|
||||||
|
.win7 .w7-toolbar button:not([role='tab']),
|
||||||
|
.win7 .w7-flat {
|
||||||
|
border-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Tabs (workspace strip) ----
|
||||||
|
7.css owns the tab CHROME; these restore the tab CONTENT layout it breaks.
|
||||||
|
`menu[role=tablist] button` sets `display: block` at (0,2,1), which
|
||||||
|
outranks Tailwind's `.flex` in `utilities` — so the icon and the label
|
||||||
|
rendered jammed together with no gap and no truncation. Restate the flex
|
||||||
|
box here (skinfix outranks skin) and leave every painted property alone.
|
||||||
|
The gap matches the gap-1.5 the template asks for. */
|
||||||
|
.win7 menu[role='tablist'] button[role='tab'] {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tabs.css hardcodes `color: #222` on every tablist button — a light-mode
|
||||||
|
literal outside 7.css's var system, which left the labels near-invisible
|
||||||
|
on the dark surface. Same failure and same remedy as the ListView and menu
|
||||||
|
literals above: route it through a token that both dark blocks override. */
|
||||||
|
.win7 menu[role='tablist'] button {
|
||||||
|
color: var(--w7-tab-c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The workspace strip scrolls horizontally when tabs overflow. Setting
|
||||||
|
`overflow-x: auto` forces the computed `overflow-y` from visible to auto
|
||||||
|
as well (CSS will not pair visible with a non-visible axis), so the strip
|
||||||
|
clips vertically — and 7.css raises the ACTIVE tab with `margin-top: -2px`,
|
||||||
|
which put its top border outside the scroll box and silently deleted it.
|
||||||
|
Reserve those 2px as padding so the pulled-up tab lands inside the box.
|
||||||
|
7.css already reserves the matching 3px on the left (padding-left: 3px)
|
||||||
|
for the same tab's -3px left margin; it just never anticipated a scroller.
|
||||||
|
Verified by pixel scan: the active tab's top edge must be #8e8f8f, not a
|
||||||
|
straight jump from the toolbar grey to the tab fill. */
|
||||||
|
.win7 .w7-toolbar menu[role='tablist'] {
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.win7 .window.glass > .title-bar {
|
||||||
|
background: var(--w7-glass-title);
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Caption text: 7.css hardcodes black + a white halo; in dark that becomes
|
||||||
|
the Ares treatment (white caption, dark halo) through the same vars. */
|
||||||
|
.win7 .title-bar-text {
|
||||||
|
color: var(--w7-cap-c);
|
||||||
|
text-shadow: var(--w7-cap-glow);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
shadlike skin — the modern, shadcn-style look
|
||||||
|
==========================================================================
|
||||||
|
|
||||||
|
Intentionally near-empty, and that is not an oversight.
|
||||||
|
|
||||||
|
This skin IS the shared foundation: the OKLCH token system, `@theme inline`,
|
||||||
|
the data-text interface sizes and the accent axis all live in ui/src/styles.css
|
||||||
|
because both skins consume them, and everything else the modern look needs is
|
||||||
|
a Tailwind utility written inline in its templates. There is no third-party
|
||||||
|
framework to bridge and no cascade inversion to undo, so there is simply
|
||||||
|
nothing left for a skin-level stylesheet to say.
|
||||||
|
|
||||||
|
Compare app/skins/7like/7like.css, which is 630 lines: all of it exists to
|
||||||
|
import 7.css above `utilities`, re-point the app's semantic tokens at Win7
|
||||||
|
values, and hand individual properties back where 7.css's dialog metrics are
|
||||||
|
wrong for this app.
|
||||||
|
|
||||||
|
This file exists so the folder is self-contained and symmetrical with the
|
||||||
|
other skin — modern-only rules that should not leak into the win7 cascade
|
||||||
|
have a home here. Rules that BOTH skins need belong in ui/src/styles.css
|
||||||
|
instead; anything added here is dead weight under .win7.
|
||||||
|
|
||||||
|
Scoping note: 7like scopes itself with the `.win7` class that 7.scoped.css
|
||||||
|
requires. This skin is the default (no marker class), so any rule added here
|
||||||
|
must NOT be written so broadly that it also matches under .win7 — prefer
|
||||||
|
scoping to the component, not to bare elements. */
|
||||||
+7
-630
@@ -9,15 +9,13 @@
|
|||||||
See the "Windows 7 skin" section at the bottom of this file. */
|
See the "Windows 7 skin" section at the bottom of this file. */
|
||||||
@layer theme, base, components, utilities, skin, skinfix;
|
@layer theme, base, components, utilities, skin, skinfix;
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
/* The SCOPED 7.css build: postcss-prefix-selector rewrote every selector to
|
/* The two skin stylesheets. Both ship in one bundle and are scoped by the
|
||||||
`.win7` (root rules) or `.win7 <sel>` (the rest), so the whole framework is
|
`.win7` class on <html>, exactly as when they lived in this file — only one
|
||||||
inert until SettingsService.applySkin puts that class on <html>. All 61
|
skin's rules can match at a time. They sit here because CSS requires every
|
||||||
icons are data URIs, so nothing needs copying into assets — and NOT the
|
@import to precede all other rules; layer assignment is fixed by the @layer
|
||||||
`7.inline.css` build, which flattens away the --w7-* vars the dark palette
|
statement above, not by this order. */
|
||||||
below depends on. The deep path is required: 7.css ships no `exports` map
|
@import "./app/skins/shadlike/shadlike.css";
|
||||||
and Tailwind's resolver only reads `style`/`mainFields`, so a bare
|
@import "./app/skins/7like/7like.css";
|
||||||
`@import "7.css"` finds no entry. */
|
|
||||||
@import "7.css/dist/7.scoped.css" layer(skin);
|
|
||||||
|
|
||||||
/* shadcn-style theme tokens. Light stays the zinc family; the dark neutrals are a
|
/* shadcn-style theme tokens. Light stays the zinc family; the dark neutrals are a
|
||||||
warm gray family (hue ~107) anchored on #1a1a19 — see the dark blocks below.
|
warm gray family (hue ~107) anchored on #1a1a19 — see the dark blocks below.
|
||||||
@@ -296,624 +294,3 @@ body {
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Windows 7 skin — SettingsService.applySkin puts `.win7` on <html>
|
|
||||||
==========================================================================
|
|
||||||
|
|
||||||
Three tiers, and the split matters:
|
|
||||||
|
|
||||||
- `@layer skin` (the 7.css import at the top of this file) owns the
|
|
||||||
CONTROLS: buttons, inputs, tabs, progress bars, tree views, the Aero
|
|
||||||
title bar. It sits ABOVE `utilities`, so a bare `button` rule in 7.css
|
|
||||||
beats `.rounded-md` on that same button. That inversion is the whole
|
|
||||||
trick — it restyles ~1900 inline utility classes with no template edits.
|
|
||||||
- This unlayered section owns the BRIDGE: it re-points the app's own
|
|
||||||
semantic tokens at Win7 values, so utilities Tailwind still controls
|
|
||||||
(bg-card, border-border, text-muted-foreground) also come out
|
|
||||||
Win7-correct. It adds NO new tokens on purpose — a new colour token
|
|
||||||
would need a matching `--color-*` line in `@theme inline`.
|
|
||||||
- `@layer skinfix` (last, above `skin`) hands individual properties BACK to
|
|
||||||
the utilities layer with `revert-layer`, for the places where 7.css's
|
|
||||||
Win7-dialog metrics are wrong for this app.
|
|
||||||
|
|
||||||
WARNING for future edits: because `skin` outranks `utilities`, any Tailwind
|
|
||||||
utility added later to a <button>, <input>, <select>, <textarea> or <table>
|
|
||||||
silently loses under this skin. `skinfix` is the only place to fix that.
|
|
||||||
|
|
||||||
Specificity notes — the blocks below must beat the token blocks above:
|
|
||||||
:root.win7 (0,2,0) beats base :root (0,1,0)
|
|
||||||
:root.win7[data-theme='dark'] (0,3,0) beats :root[data-theme=…] (0,2,0)
|
|
||||||
and ties the darker-tone block (0,3,0),
|
|
||||||
winning on source order (it is last).
|
|
||||||
The accent formula blocks are deliberately NOT overridden: they only set
|
|
||||||
--primary/--ring/--cursor-tint, and letting them through is what makes the
|
|
||||||
accent picker recolour the Aero chrome — authentic, Win7 shipped exactly
|
|
||||||
that ("Window Color" personalization). */
|
|
||||||
|
|
||||||
/* Segoe UI is not on macOS/Linux and is not redistributable. Noto Sans is the
|
|
||||||
substitute 7.css itself names in its --w7-font fallback stack. Vendored
|
|
||||||
(OFL-1.1) from @fontsource/noto-sans@5.3.0, files/noto-sans-latin-{400,700}
|
|
||||||
-normal.woff2 -> ui/src/fonts/. Vendored rather than @imported from
|
|
||||||
node_modules because Tailwind inlines imported CSS, which would leave
|
|
||||||
@fontsource's relative url() rebased against the wrong directory. They live
|
|
||||||
under src/ (not public/) so esbuild emits them as hashed assets and rewrites
|
|
||||||
the url() — a public/ path is not resolvable at build time. */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Noto Sans App';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: block;
|
|
||||||
src: url('./fonts/noto-sans-latin-400-normal.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Noto Sans App';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: block;
|
|
||||||
src: url('./fonts/noto-sans-latin-700-normal.woff2') format('woff2');
|
|
||||||
}
|
|
||||||
|
|
||||||
:root.win7 {
|
|
||||||
/* Real Segoe first (Windows), bundled substitute everywhere else. */
|
|
||||||
--w7-family: 'Segoe UI', 'SegoeUI', 'Noto Sans App', system-ui, sans-serif;
|
|
||||||
/* ~7.css's 9pt, but in px so it tracks the interface-size axis below.
|
|
||||||
Restating the `font` SHORTHAND (not just the family) is deliberate:
|
|
||||||
7.css applies `font: var(--w7-font)` on 12 component rules, and the
|
|
||||||
shorthand also resets weight and line-height — pinning 400/1.4 here stops
|
|
||||||
those rules from flattening the app's `font-medium` / leading. */
|
|
||||||
--w7-fs: 12px;
|
|
||||||
--w7-font: 400 var(--w7-fs) / 1.4 var(--w7-family);
|
|
||||||
|
|
||||||
/* Surface ramp, Win7 Explorer. This INVERTS the modern ramp's direction:
|
|
||||||
there --chrome (frame strips) sits below --background (content panes);
|
|
||||||
here the content pane is the brightest thing on screen and the frame is
|
|
||||||
the #f0f0f0 window surface. Deliberate — that is what Explorer was. */
|
|
||||||
--background: #ffffff;
|
|
||||||
--chrome: #f0f0f0;
|
|
||||||
--card: #f0f0f0;
|
|
||||||
--foreground: #000000;
|
|
||||||
--card-foreground: #000000;
|
|
||||||
--muted-foreground: #6d6d6d;
|
|
||||||
--secondary: #e8e8e8;
|
|
||||||
--secondary-foreground: #000000;
|
|
||||||
--muted: #e8e8e8;
|
|
||||||
/* Win7's hover is a pale blue wash, not a grey — matches --w7-el-grad-h */
|
|
||||||
--accent: #eaf6fd;
|
|
||||||
--accent-foreground: #000000;
|
|
||||||
--border: #d5d5d5;
|
|
||||||
--input: #abadb3; /* 7.css's own text-input border */
|
|
||||||
--destructive: #c62828;
|
|
||||||
--destructive-foreground: #ffffff;
|
|
||||||
--success: #06b025; /* the Win7 progress-bar green */
|
|
||||||
|
|
||||||
/* 5px, NOT 3px: `rounded-md` is 74 of the 112 radius utilities in the
|
|
||||||
templates and compiles to calc(var(--radius) - 2px), so 5 lands it exactly
|
|
||||||
on --w7-el-bdr (3px). --radius itself feeds `rounded-lg` (12 uses) at 5px,
|
|
||||||
near the 6px window radius. Bare `rounded` (26 uses) is a Tailwind literal
|
|
||||||
0.25rem and is unaffected. */
|
|
||||||
--radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Interface size: Win7 shipped this exact axis ("Smaller / Medium / Larger"),
|
|
||||||
so scaling is more authentic than disabling it. One indirection covers all
|
|
||||||
three tiers because var substitution is lazy. The --row-h / --list-font
|
|
||||||
block further up scales the file list independently and is untouched. */
|
|
||||||
:root.win7[data-text='compact'] { --w7-fs: 11px; }
|
|
||||||
:root.win7[data-text='large'] { --w7-fs: 13px; }
|
|
||||||
|
|
||||||
/* Light values for every 7.css literal the skinfix rules below restate through
|
|
||||||
vars (7.css hardcodes these outside its var system). The values are 7.css's
|
|
||||||
own, verbatim — light must render exactly as stock. Dark overrides live in
|
|
||||||
the duplicated dark blocks above, which is the house pattern: rules are
|
|
||||||
written ONCE and theme through tokens. (The previous attribute-keyed dark
|
|
||||||
rules silently missed SYSTEM-dark — the media-query path sets no attribute.) */
|
|
||||||
:root.win7 {
|
|
||||||
/* ListView (listview.css) */
|
|
||||||
--w7-lv-bg: #fff;
|
|
||||||
--w7-lv-bd: #c0c1cd;
|
|
||||||
--w7-lv-c: #000;
|
|
||||||
--w7-lv-head-grad: linear-gradient(180deg, #fff 45%, #fafafa 0, #f0f0f0);
|
|
||||||
--w7-lv-head-bd: #d7d7d7;
|
|
||||||
--w7-lv-cell-bd: #eee;
|
|
||||||
/* Tabs (tabs.css). 7.css hardcodes `color: #222` on every tablist button,
|
|
||||||
outside its own var system, which is unreadable on the dark surface. */
|
|
||||||
--w7-tab-c: #222;
|
|
||||||
/* Menu (menu.css) */
|
|
||||||
--w7-mn-c: #000;
|
|
||||||
--w7-mn-bd: #0006;
|
|
||||||
--w7-mn-stripe: inset 1px 0 #00000026, inset -1px 0 #fff;
|
|
||||||
--w7-mn-divider: inset 0 1px #00000026, inset 0 -1px #fff;
|
|
||||||
--w7-mn-check-ring: #b3d3f9;
|
|
||||||
--w7-mn-check-c: #0c12a1;
|
|
||||||
/* Glass caption streaks (window.css .window.glass>.title-bar): a literal.
|
|
||||||
The two #fff5 corner shines wash a DARK caption silver, so dark subdues
|
|
||||||
every white in the stack while keeping the streak structure. */
|
|
||||||
--w7-glass-title: linear-gradient(135deg, #fff5 70px, transparent 100px), linear-gradient(225deg, #fff5 70px, transparent 100px), linear-gradient(54deg, #0002 0 4%, #6661 6% 6%, #0002 8% 10%, #0002 15% 16%, #aaa1 17% 18%, #0002 23% 24%, #bbb2 25% 26%, #0002 31% 33%, #0002 34% 34.5%, #bbb2 36% 40%, #0002 41% 41.5%, #bbb2 44% 45%, #bbb2 46% 47%, #0002 48% 49%, #0002 50% 50.5%, #0002 56% 56.5%, #bbb2 57% 63%, #0002 67% 69%, #bbb2 69.5% 70%, #0002 73.5% 74%, #bbb2 74.5% 79%, #0002 80% 84%, #aaa2 85% 86%, #0002 87%, #bbb1 90%) 0 / 100vw 100vh no-repeat fixed;
|
|
||||||
/* Caption text (window.css .title-bar-text: black + 8-fold white halo) */
|
|
||||||
--w7-cap-c: #000;
|
|
||||||
--w7-cap-glow: 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff,
|
|
||||||
0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff, 0 0 10px #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hand-authored dark Win7. Microsoft never shipped one — this exists so the
|
|
||||||
theme and tone settings stay live under the skin. Only the colour-carrying
|
|
||||||
--w7-* vars are listed: --w7-el-grad and --w7-w-grad are DERIVED from the
|
|
||||||
values below and recolour for free, while --w7-el-grad-h/-a and
|
|
||||||
--w7-li-bg-hl are literal hex in 7.css and so must be restated. The ~25
|
|
||||||
geometry vars and ~20 base64 icon vars are theme-independent.
|
|
||||||
DUPLICATED for forced-dark and system-dark, per the rule the token blocks
|
|
||||||
above set: every dark value goes in BOTH. */
|
|
||||||
:root.win7[data-theme='dark'] {
|
|
||||||
--w7-surface: #2b2b2b;
|
|
||||||
--w7-el-bg: #3a3a3a;
|
|
||||||
--w7-el-bg-d: #333333;
|
|
||||||
--w7-el-bg-s-1: #333333;
|
|
||||||
--w7-el-bg-s-2: #262626;
|
|
||||||
--w7-el-bd: #565656;
|
|
||||||
--w7-el-bd-h: #4a8cbe;
|
|
||||||
--w7-el-bd-a: #3f6c8c;
|
|
||||||
--w7-el-bd-d: #3f3f3f;
|
|
||||||
--w7-el-c: #f0f0f0;
|
|
||||||
--w7-el-c-d: #6f6f6f;
|
|
||||||
--w7-el-sd: inset 0 0 0 1px #ffffff14;
|
|
||||||
--w7-el-grad-h: linear-gradient(#3c5a6b 45%, #315062 45%, #2b4557);
|
|
||||||
--w7-el-grad-a: linear-gradient(#2c4657, #24384a 30% 50%, #1d2f3f 50%, #16242f);
|
|
||||||
--w7-li-bd-hl: #3f6c8c;
|
|
||||||
--w7-li-bg-hl: linear-gradient(#ffffff14, #4a6b8433 90%, #ffffff1a);
|
|
||||||
--w7-w-bd: #000000e6;
|
|
||||||
/* Ares-style black glass: near-black ground; 7.css's own alpha streaks and
|
|
||||||
highlight gradients become the gloss. Blue lives in accents (selection,
|
|
||||||
active tab), not in the glass itself. */
|
|
||||||
--w7-w-bg: #101013;
|
|
||||||
--w7-tab-bg: #1c1c1e;
|
|
||||||
--w7-title-c: #f5f5f5;
|
|
||||||
--w7-link-c: #6fb3ff;
|
|
||||||
/* literal restatements consumed by the skinfix rules (see :root.win7) */
|
|
||||||
--w7-lv-bg: var(--w7-el-bg-s-2);
|
|
||||||
--w7-lv-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-lv-c: var(--w7-el-c);
|
|
||||||
--w7-lv-head-grad: linear-gradient(180deg, var(--w7-el-bg) 45%, var(--w7-el-bg-d) 0, var(--w7-el-bg-s-1));
|
|
||||||
--w7-lv-head-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-lv-cell-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-tab-c: var(--w7-el-c);
|
|
||||||
--w7-mn-c: var(--w7-el-c);
|
|
||||||
--w7-mn-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-mn-stripe: inset 1px 0 #00000059, inset -1px 0 #ffffff14;
|
|
||||||
--w7-mn-divider: inset 0 1px #00000059, inset 0 -1px #ffffff14;
|
|
||||||
--w7-mn-check-ring: var(--w7-li-bd-hl);
|
|
||||||
--w7-mn-check-c: var(--w7-el-c);
|
|
||||||
--w7-glass-title: linear-gradient(135deg, #ffffff1a 70px, transparent 100px), linear-gradient(225deg, #ffffff1a 70px, transparent 100px), linear-gradient(54deg, #0003 0 4%, #ffffff08 6% 6%, #0003 8% 10%, #0003 15% 16%, #ffffff0d 17% 18%, #0003 23% 24%, #ffffff10 25% 26%, #0003 31% 33%, #0003 34% 34.5%, #ffffff10 36% 40%, #0003 41% 41.5%, #ffffff10 44% 45%, #ffffff10 46% 47%, #0003 48% 49%, #0003 50% 50.5%, #0003 56% 56.5%, #ffffff10 57% 63%, #0003 67% 69%, #ffffff10 69.5% 70%, #0003 73.5% 74%, #ffffff10 74.5% 79%, #0003 80% 84%, #ffffff0d 85% 86%, #0003 87%, #ffffff0a 90%) 0 / 100vw 100vh no-repeat fixed;
|
|
||||||
/* Ares: white caption on black glass, dark halo instead of white */
|
|
||||||
--w7-cap-c: var(--w7-title-c);
|
|
||||||
--w7-cap-glow: 0 0 8px #000c, 0 0 8px #000c, 0 1px 2px #000;
|
|
||||||
--w7-link-c-h: #9acbff;
|
|
||||||
|
|
||||||
--background: #1f1f1f;
|
|
||||||
--chrome: #2b2b2b;
|
|
||||||
--card: #2b2b2b;
|
|
||||||
--foreground: #f0f0f0;
|
|
||||||
--card-foreground: #f0f0f0;
|
|
||||||
--muted-foreground: #a0a0a0;
|
|
||||||
--secondary: #383838;
|
|
||||||
--secondary-foreground: #f0f0f0;
|
|
||||||
--muted: #383838;
|
|
||||||
--accent: #2f4a5c;
|
|
||||||
--accent-foreground: #f0f0f0;
|
|
||||||
--border: #3c3c3c;
|
|
||||||
--input: #55585c;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root.win7:not([data-theme='light']) {
|
|
||||||
--w7-surface: #2b2b2b;
|
|
||||||
--w7-el-bg: #3a3a3a;
|
|
||||||
--w7-el-bg-d: #333333;
|
|
||||||
--w7-el-bg-s-1: #333333;
|
|
||||||
--w7-el-bg-s-2: #262626;
|
|
||||||
--w7-el-bd: #565656;
|
|
||||||
--w7-el-bd-h: #4a8cbe;
|
|
||||||
--w7-el-bd-a: #3f6c8c;
|
|
||||||
--w7-el-bd-d: #3f3f3f;
|
|
||||||
--w7-el-c: #f0f0f0;
|
|
||||||
--w7-el-c-d: #6f6f6f;
|
|
||||||
--w7-el-sd: inset 0 0 0 1px #ffffff14;
|
|
||||||
--w7-el-grad-h: linear-gradient(#3c5a6b 45%, #315062 45%, #2b4557);
|
|
||||||
--w7-el-grad-a: linear-gradient(#2c4657, #24384a 30% 50%, #1d2f3f 50%, #16242f);
|
|
||||||
--w7-li-bd-hl: #3f6c8c;
|
|
||||||
--w7-li-bg-hl: linear-gradient(#ffffff14, #4a6b8433 90%, #ffffff1a);
|
|
||||||
--w7-w-bd: #000000e6;
|
|
||||||
--w7-w-bg: #101013;
|
|
||||||
--w7-tab-bg: #1c1c1e;
|
|
||||||
--w7-title-c: #f5f5f5;
|
|
||||||
--w7-link-c: #6fb3ff;
|
|
||||||
/* literal restatements consumed by the skinfix rules (see :root.win7) */
|
|
||||||
--w7-lv-bg: var(--w7-el-bg-s-2);
|
|
||||||
--w7-lv-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-lv-c: var(--w7-el-c);
|
|
||||||
--w7-lv-head-grad: linear-gradient(180deg, var(--w7-el-bg) 45%, var(--w7-el-bg-d) 0, var(--w7-el-bg-s-1));
|
|
||||||
--w7-lv-head-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-lv-cell-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-tab-c: var(--w7-el-c);
|
|
||||||
--w7-mn-c: var(--w7-el-c);
|
|
||||||
--w7-mn-bd: var(--w7-el-bd-d);
|
|
||||||
--w7-mn-stripe: inset 1px 0 #00000059, inset -1px 0 #ffffff14;
|
|
||||||
--w7-mn-divider: inset 0 1px #00000059, inset 0 -1px #ffffff14;
|
|
||||||
--w7-mn-check-ring: var(--w7-li-bd-hl);
|
|
||||||
--w7-mn-check-c: var(--w7-el-c);
|
|
||||||
--w7-glass-title: linear-gradient(135deg, #ffffff1a 70px, transparent 100px), linear-gradient(225deg, #ffffff1a 70px, transparent 100px), linear-gradient(54deg, #0003 0 4%, #ffffff08 6% 6%, #0003 8% 10%, #0003 15% 16%, #ffffff0d 17% 18%, #0003 23% 24%, #ffffff10 25% 26%, #0003 31% 33%, #0003 34% 34.5%, #ffffff10 36% 40%, #0003 41% 41.5%, #ffffff10 44% 45%, #ffffff10 46% 47%, #0003 48% 49%, #0003 50% 50.5%, #0003 56% 56.5%, #ffffff10 57% 63%, #0003 67% 69%, #ffffff10 69.5% 70%, #0003 73.5% 74%, #ffffff10 74.5% 79%, #0003 80% 84%, #ffffff0d 85% 86%, #0003 87%, #ffffff0a 90%) 0 / 100vw 100vh no-repeat fixed;
|
|
||||||
/* Ares: white caption on black glass, dark halo instead of white */
|
|
||||||
--w7-cap-c: var(--w7-title-c);
|
|
||||||
--w7-cap-glow: 0 0 8px #000c, 0 0 8px #000c, 0 1px 2px #000;
|
|
||||||
--w7-link-c-h: #9acbff;
|
|
||||||
|
|
||||||
--background: #1f1f1f;
|
|
||||||
--chrome: #2b2b2b;
|
|
||||||
--card: #2b2b2b;
|
|
||||||
--foreground: #f0f0f0;
|
|
||||||
--card-foreground: #f0f0f0;
|
|
||||||
--muted-foreground: #a0a0a0;
|
|
||||||
--secondary: #383838;
|
|
||||||
--secondary-foreground: #f0f0f0;
|
|
||||||
--muted: #383838;
|
|
||||||
--accent: #2f4a5c;
|
|
||||||
--accent-foreground: #f0f0f0;
|
|
||||||
--border: #3c3c3c;
|
|
||||||
--input: #55585c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark tone "darker": Aero has no meaningful true black, so rather than leave
|
|
||||||
the Tone row a dead control the skin just goes a step deeper. Duplicated
|
|
||||||
forced + system like everything else. */
|
|
||||||
:root.win7[data-theme='dark'][data-dark-tone='darker'] {
|
|
||||||
--w7-surface: #1a1a1a;
|
|
||||||
--w7-el-bg: #262626;
|
|
||||||
--w7-w-bg: #060607;
|
|
||||||
--background: #121212;
|
|
||||||
--chrome: #1a1a1a;
|
|
||||||
--card: #1a1a1a;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root.win7:not([data-theme='light'])[data-dark-tone='darker'] {
|
|
||||||
--w7-surface: #1a1a1a;
|
|
||||||
--w7-el-bg: #262626;
|
|
||||||
--w7-w-bg: #060607;
|
|
||||||
--background: #121212;
|
|
||||||
--chrome: #1a1a1a;
|
|
||||||
--card: #1a1a1a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Accent -> Aero window colour, so the existing accent picker keeps meaning
|
|
||||||
under the skin. --w7-w-bg is the one colour 7.css documents as settable.
|
|
||||||
LIGHT ONLY: in dark the glass is Ares black regardless of accent (the accent
|
|
||||||
keeps tinting selection etc. through --primary). These arms out-rank the
|
|
||||||
accent rule below on specificity — without them it wins the tie on source
|
|
||||||
order and dark glass turns accent-coloured. */
|
|
||||||
:root.win7[data-accent] {
|
|
||||||
--w7-w-bg: var(--primary);
|
|
||||||
}
|
|
||||||
:root.win7[data-theme='dark'][data-accent] {
|
|
||||||
--w7-w-bg: #101013;
|
|
||||||
}
|
|
||||||
:root.win7[data-theme='dark'][data-dark-tone='darker'][data-accent] {
|
|
||||||
--w7-w-bg: #060607;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root.win7:not([data-theme='light'])[data-accent] {
|
|
||||||
--w7-w-bg: #101013;
|
|
||||||
}
|
|
||||||
:root.win7:not([data-theme='light'])[data-dark-tone='darker'][data-accent] {
|
|
||||||
--w7-w-bg: #060607;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unlayered on purpose: the html/body rule above sets font-family unlayered,
|
|
||||||
so a layered override would lose. Family only — NOT font-size: the app's
|
|
||||||
rem-based utilities are root-relative, and shifting the root would move
|
|
||||||
every line box for no gain. */
|
|
||||||
.win7 body {
|
|
||||||
font-family: var(--w7-family);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The win7 window is a FRAMELESS, natively transparent window whose only
|
|
||||||
opaque surface is meant to be `.window` — that is the whole premise of the
|
|
||||||
6px rounded frame, since the area outside the radius must let the desktop
|
|
||||||
through. The unlayered `html, body { background: var(--background) }` above
|
|
||||||
broke that premise silently: it painted an opaque rectangle behind the
|
|
||||||
frame, so the rounded corners had an opaque ground filling them in and the
|
|
||||||
window read as a rectangle with a drawn-on curve.
|
|
||||||
Unlayered on purpose, same reason as the font-family rule: the html/body
|
|
||||||
background it overrides is itself unlayered, and unlayered declarations beat
|
|
||||||
every @layer regardless of specificity — a `skinfix` rule could not touch
|
|
||||||
it. Scoped to .win7 so the modern skin keeps its opaque ground. */
|
|
||||||
html.win7,
|
|
||||||
html.win7 body {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Neutralizers live in `skin`, NOT `skinfix`, and this is load-bearing:
|
|
||||||
`revert-layer` rolls a property back to the previous LAYER, so from inside
|
|
||||||
`skin` it lands on `utilities` (the Tailwind classes already in the
|
|
||||||
templates) — which is the whole point. From `skinfix` it would land back on
|
|
||||||
7.css and do nothing. Within `skin` these beat the imported rules on equal
|
|
||||||
specificity by source order, since the import is at the top of this file. */
|
|
||||||
@layer skin {
|
|
||||||
/* 7.css sizes every <button> like a Win7 dialog push button
|
|
||||||
(min-width 75px, min-height 23px, padding 0 12px). Most of this app's 84
|
|
||||||
buttons are ~24px icon squares sized by utilities, so without this the tab
|
|
||||||
bar wraps and the options popover overflows. Hand the BOX MODEL back to
|
|
||||||
the utilities layer and keep 7.css's paint. The title bar's own controls
|
|
||||||
are matched by a MORE specific 7.css selector (.title-bar-controls button)
|
|
||||||
and so keep their Aero glyph metrics untouched. */
|
|
||||||
.win7 button {
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
padding: revert-layer;
|
|
||||||
text-align: revert-layer;
|
|
||||||
color: revert-layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 7.css hardcodes #fff / #abadb3 / #222 on form controls and tables, outside
|
|
||||||
its own var system. Reverting routes them back through the bridged tokens
|
|
||||||
above, which is what makes dark mode nearly free. */
|
|
||||||
.win7 input[type='text'],
|
|
||||||
.win7 input[type='password'],
|
|
||||||
.win7 input[type='number'],
|
|
||||||
.win7 input[type='search'],
|
|
||||||
.win7 textarea {
|
|
||||||
background-color: revert-layer;
|
|
||||||
border-color: revert-layer;
|
|
||||||
}
|
|
||||||
.win7 table {
|
|
||||||
background-color: revert-layer;
|
|
||||||
color: revert-layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Win7 toolbar buttons are FLAT with a hover glow, not Aero slabs —
|
|
||||||
Explorer's command bar worked exactly that way. `w7-toolbar` marks the
|
|
||||||
three header rows; a plain `.title-bar` sibling is deliberately NOT one of
|
|
||||||
them, so its controls keep the real Aero treatment. `w7-flat` is the
|
|
||||||
per-button escape hatch. Both classes are inert under the modern skin.
|
|
||||||
`:not([role='tab'])` is load-bearing: the workspace strip IS a w7-toolbar,
|
|
||||||
so without the exemption this rule would strip the border, gradient and
|
|
||||||
both pseudo-elements off every tab and leave 7.css's tab component drawing
|
|
||||||
nothing. Tabs want the full Aero slab; the +/Connections/options buttons
|
|
||||||
beside them want the flat treatment.
|
|
||||||
Only `background` stays here — it is the one property that needs to land
|
|
||||||
on `utilities` via revert-layer. The literals that suppress the Aero slab
|
|
||||||
moved to `skinfix`, because 7.css restates border-color at
|
|
||||||
`.win7 button:not(:disabled):not(:hover)` = (0,3,1), which outranks
|
|
||||||
`.win7 .w7-flat` = (0,2,0). The toolbar arm only ever won that fight by
|
|
||||||
tying on specificity and landing later in the same layer — an accident,
|
|
||||||
and one w7-flat did not share: it rendered a bordered slab regardless. */
|
|
||||||
.win7 .w7-toolbar button:not([role='tab']),
|
|
||||||
.win7 .w7-flat {
|
|
||||||
background: revert-layer;
|
|
||||||
}
|
|
||||||
.win7 .w7-toolbar button:not([role='tab'])::before,
|
|
||||||
.win7 .w7-toolbar button:not([role='tab'])::after,
|
|
||||||
.win7 .w7-flat::before,
|
|
||||||
.win7 .w7-flat::after {
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Above `skin`, for rules that must state a value rather than defer to a
|
|
||||||
neighbouring layer. */
|
|
||||||
@layer skinfix {
|
|
||||||
/* Opt back IN to real Win7 push-button metrics, for the dialog and options
|
|
||||||
buttons where they are right. */
|
|
||||||
.win7 .w7-btn {
|
|
||||||
min-width: 75px;
|
|
||||||
min-height: 23px;
|
|
||||||
padding: 0 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- ListView (file panels) ----
|
|
||||||
7.css's listview.css hardcodes light literals (#fff, #c0c1cd, #eee and the
|
|
||||||
white header gradient) instead of --w7-* vars, so unlike the rest of the
|
|
||||||
skin it does NOT follow the hand-authored dark palette — the header labels
|
|
||||||
came out invisible, dark on dark. These restate the same rules in dark
|
|
||||||
terms; light keeps 7.css's own values untouched.
|
|
||||||
|
|
||||||
CRITICAL: the base surface goes on `table`, NEVER on `tbody > tr > *`.
|
|
||||||
7.css paints the selection band on the ROW, and a cell background paints
|
|
||||||
over a row background — setting one erased the selection highlight
|
|
||||||
entirely in dark mode. */
|
|
||||||
.win7 table {
|
|
||||||
background-color: var(--w7-lv-bg);
|
|
||||||
border-color: var(--w7-lv-bd);
|
|
||||||
color: var(--w7-lv-c);
|
|
||||||
}
|
|
||||||
.win7 table > thead > tr > * {
|
|
||||||
background: var(--w7-lv-head-grad);
|
|
||||||
border-color: var(--w7-lv-head-bd);
|
|
||||||
color: var(--w7-lv-c);
|
|
||||||
}
|
|
||||||
.win7 table > tbody > tr > :not(:last-child) {
|
|
||||||
border-right-color: var(--w7-lv-cell-bd);
|
|
||||||
}
|
|
||||||
/* 7.css drops the cell separators inside a selected row; the rule above is
|
|
||||||
more specific than its, so restore that behaviour explicitly. */
|
|
||||||
.win7 table > tbody > tr.highlighted > :not(:last-child) {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
.win7 table > tbody > tr.highlighted {
|
|
||||||
background: var(--w7-li-bg-hl);
|
|
||||||
border-color: var(--w7-li-bd-hl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Window frame ----
|
|
||||||
The native window is transparent (chromeOptions in src/bun/windows.ts), and
|
|
||||||
nothing but .window may paint an opaque surface — that is what lets the 6px
|
|
||||||
corner radius actually be transparent at the corners. That invariant is NOT
|
|
||||||
free: html/body paint an opaque background unlayered, and had to be reset
|
|
||||||
to transparent for .win7 (see the rule above @layer skin). Anything else
|
|
||||||
that paints full-bleed will fill the corners the same way.
|
|
||||||
NO drop shadow, by request: 7.css's `2px 2px 10px 1px #000000b3` needed a
|
|
||||||
margin gutter to fall into and still read heavy against a real desktop, so
|
|
||||||
the frame keeps only the inset 1px glass highlight from that shorthand
|
|
||||||
(dropping the whole box-shadow would lose the highlight too — it lives in
|
|
||||||
the same declaration). No shadow -> no gutter -> full-bleed 100vh.
|
|
||||||
Keyed on `w7-frame` — the app hook the three window ROOTS carry — never on
|
|
||||||
bare .window: on any other .window (dialogs, demos) this sizing is
|
|
||||||
nonsense and 7.css's own shadow is fine. */
|
|
||||||
.win7 .window.w7-frame {
|
|
||||||
margin: 0;
|
|
||||||
height: 100vh;
|
|
||||||
box-shadow: inset 0 0 0 1px #fffa;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A maximized Win7 window has SQUARE corners — the radius only exists to let
|
|
||||||
the desktop show past a floating frame, and a rounded maximized window
|
|
||||||
leaves four notches of desktop in the screen corners.
|
|
||||||
Read off the title bar's own `is-maximized` (title-bar.ts publishes it on
|
|
||||||
its host) with :has(), so the frame reacts without a second windowState
|
|
||||||
subscription. .title-bar is a DIRECT child of .window — app-title-bar
|
|
||||||
carries the class on its host element, which is why `>` holds here.
|
|
||||||
The radius is declared three times in 7.css — on .window and on both the
|
|
||||||
::before tint and the ::after glass layer — so all three must be zeroed or
|
|
||||||
a rounded pseudo-element still cuts the corner off the square frame. */
|
|
||||||
.win7 .window.w7-frame:has(> .title-bar.is-maximized),
|
|
||||||
.win7 .window.w7-frame:has(> .title-bar.is-maximized)::before,
|
|
||||||
.win7 .window.w7-frame:has(> .title-bar.is-maximized)::after {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 7.css's glass layer is `backdrop-filter: blur(4px)` on .window::after —
|
|
||||||
but CSS cannot sample the DESKTOP behind a transparent native window
|
|
||||||
(there is no backdrop to read), and WebKitGTK paints the unsampleable
|
|
||||||
layer BLACK: every see-through part of the glass — the frame ring, the
|
|
||||||
area around the title-bar buttons — composited over black instead of over
|
|
||||||
the desktop. With it off, the real desktop shows through the glass tint,
|
|
||||||
which is closer to Aero than a blur we cannot have. (True blur-behind
|
|
||||||
needs compositor support; GNOME offers none to X11/XWayland clients.) */
|
|
||||||
.win7 .window.glass::after {
|
|
||||||
-webkit-backdrop-filter: none;
|
|
||||||
backdrop-filter: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Constant row geometry. 7.css gives ONLY tr.highlighted a 1px border, and
|
|
||||||
the table is border-collapse: collapse — so a selected row physically grew
|
|
||||||
1px at top and bottom and shoved every row below it down by 2px as the
|
|
||||||
cursor moved. Every row reserves that border instead, transparent when not
|
|
||||||
selected, so selecting changes colour and nothing else.
|
|
||||||
NOTE: this sits in `skinfix`, which outranks `skin` by LAYER regardless of
|
|
||||||
specificity, so it would also blank 7.css's highlight border — both
|
|
||||||
selected states therefore restate their border-color below. */
|
|
||||||
.win7 table > tbody > tr {
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
.win7 table > tbody > tr.highlighted {
|
|
||||||
border-color: var(--w7-li-bd-hl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pane-focus model (commander convention, matching Win7 Explorer):
|
|
||||||
the CURSOR ROW always carries the selection band in BOTH panels —
|
|
||||||
tr.highlighted covers "selected via Space" AND "cursor row" (the template
|
|
||||||
ORs them). What changes with pane focus is the band's strength, never
|
|
||||||
whether it exists:
|
|
||||||
- focused pane -> strong Aero band (below)
|
|
||||||
- unfocused pane -> 7.css's own tr.highlighted, which IS Win7's
|
|
||||||
inactive-selection wash — faint by design, not a bug.
|
|
||||||
The dotted focus rectangle is NOT the focus signal; it is layered on top
|
|
||||||
of the band in the focused pane only, the way Win7 drew the keyboard
|
|
||||||
focus rect over the selected item. */
|
|
||||||
.win7 table.is-active > tbody > tr.highlighted {
|
|
||||||
background: var(--w7-el-grad-h);
|
|
||||||
border-color: var(--w7-el-bd-h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* One dotted rectangle around the ROW; per cell (tr > *) draws a dotted box
|
|
||||||
around every cell instead — visibly wrong. */
|
|
||||||
.win7 table > tbody > tr.is-cursor {
|
|
||||||
outline: 1px dotted var(--w7-el-c);
|
|
||||||
outline-offset: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- 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 ul[role='menu'] {
|
|
||||||
color: var(--w7-mn-c);
|
|
||||||
border-color: var(--w7-mn-bd);
|
|
||||||
}
|
|
||||||
.win7 ul[role='menu']:before {
|
|
||||||
box-shadow: var(--w7-mn-stripe);
|
|
||||||
}
|
|
||||||
.win7 ul [role='menuitem'].has-divider:after {
|
|
||||||
box-shadow: var(--w7-mn-divider);
|
|
||||||
}
|
|
||||||
.win7 ul [role='menuitem'] > input[type] + label:before {
|
|
||||||
box-shadow: 0 0 0 1px var(--w7-mn-check-ring);
|
|
||||||
}
|
|
||||||
.win7 ul [role='menuitem'] > input[type='checkbox']:checked + label:after {
|
|
||||||
color: var(--w7-mn-check-c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Flat buttons (toolbars + the w7-flat escape hatch) ----
|
|
||||||
The paint-suppression half of the rule in `skin` (see there for the split).
|
|
||||||
It lives in `skinfix` because 7.css restates border-color at (0,3,1) via
|
|
||||||
`.win7 button:not(:disabled):not(:hover)` and again on :hover, which no
|
|
||||||
reasonable `.w7-flat` selector outranks — layer order settles it instead
|
|
||||||
of a specificity arms race. */
|
|
||||||
.win7 .w7-toolbar button:not([role='tab']),
|
|
||||||
.win7 .w7-flat {
|
|
||||||
border-color: transparent;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Tabs (workspace strip) ----
|
|
||||||
7.css owns the tab CHROME; these restore the tab CONTENT layout it breaks.
|
|
||||||
`menu[role=tablist] button` sets `display: block` at (0,2,1), which
|
|
||||||
outranks Tailwind's `.flex` in `utilities` — so the icon and the label
|
|
||||||
rendered jammed together with no gap and no truncation. Restate the flex
|
|
||||||
box here (skinfix outranks skin) and leave every painted property alone.
|
|
||||||
The gap matches the gap-1.5 the template asks for. */
|
|
||||||
.win7 menu[role='tablist'] button[role='tab'] {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tabs.css hardcodes `color: #222` on every tablist button — a light-mode
|
|
||||||
literal outside 7.css's var system, which left the labels near-invisible
|
|
||||||
on the dark surface. Same failure and same remedy as the ListView and menu
|
|
||||||
literals above: route it through a token that both dark blocks override. */
|
|
||||||
.win7 menu[role='tablist'] button {
|
|
||||||
color: var(--w7-tab-c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The workspace strip scrolls horizontally when tabs overflow. Setting
|
|
||||||
`overflow-x: auto` forces the computed `overflow-y` from visible to auto
|
|
||||||
as well (CSS will not pair visible with a non-visible axis), so the strip
|
|
||||||
clips vertically — and 7.css raises the ACTIVE tab with `margin-top: -2px`,
|
|
||||||
which put its top border outside the scroll box and silently deleted it.
|
|
||||||
Reserve those 2px as padding so the pulled-up tab lands inside the box.
|
|
||||||
7.css already reserves the matching 3px on the left (padding-left: 3px)
|
|
||||||
for the same tab's -3px left margin; it just never anticipated a scroller.
|
|
||||||
Verified by pixel scan: the active tab's top edge must be #8e8f8f, not a
|
|
||||||
straight jump from the toolbar grey to the tab fill. */
|
|
||||||
.win7 .w7-toolbar menu[role='tablist'] {
|
|
||||||
padding-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.win7 .window.glass > .title-bar {
|
|
||||||
background: var(--w7-glass-title);
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Caption text: 7.css hardcodes black + a white halo; in dark that becomes
|
|
||||||
the Ares treatment (white caption, dark halo) through the same vars. */
|
|
||||||
.win7 .title-bar-text {
|
|
||||||
color: var(--w7-cap-c);
|
|
||||||
text-shadow: var(--w7-cap-glow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user