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:
+7
-630
@@ -9,15 +9,13 @@
|
||||
See the "Windows 7 skin" section at the bottom of this file. */
|
||||
@layer theme, base, components, utilities, skin, skinfix;
|
||||
@import "tailwindcss";
|
||||
/* 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);
|
||||
/* The two skin stylesheets. Both ship in one bundle and are scoped by the
|
||||
`.win7` class on <html>, exactly as when they lived in this file — only one
|
||||
skin's rules can match at a time. They sit here because CSS requires every
|
||||
@import to precede all other rules; layer assignment is fixed by the @layer
|
||||
statement above, not by this order. */
|
||||
@import "./app/skins/shadlike/shadlike.css";
|
||||
@import "./app/skins/7like/7like.css";
|
||||
|
||||
/* 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.
|
||||
@@ -296,624 +294,3 @@ body {
|
||||
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