From a8cb8129f188db9d1ef6c32afb7b8759a36eb5a7 Mon Sep 17 00:00:00 2001 From: tPenguinLTG Date: Wed, 22 Apr 2020 19:56:48 -0400 Subject: [PATCH 1/4] Support color themes Support the full range of configurable colors in a Windows Classic theme. The colors are configured using CSS variables named after the corresponding Windows theme file property converted from PascalCase to kebab-case. Colors are given in rgb format to make it easier to convert and recognize values from a Windows theme file. The old variables are kept for backwards compatibility and are mapped accordingly. However, the old `--button-face`, which maps to `--button-light`, has not been mapped because of a name conflict with the new `--button-face`; this is a breaking change. --- style.css | 227 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 145 insertions(+), 82 deletions(-) diff --git a/style.css b/style.css index d5a6af2..95ee92c 100644 --- a/style.css +++ b/style.css @@ -6,16 +6,46 @@ :root { /* Color */ - --surface: #c0c0c0; - --button-highlight: #ffffff; - --button-face: #dfdfdf; - --button-shadow: #808080; - --window-frame: #0a0a0a; - --dialog-blue: #000080; - --dialog-blue-light: #1084d0; - --dialog-gray: #808080; - --dialog-gray-light: #b5b5b5; - --link-blue: #0000ff; + /* Legacy variables for backwards compatibility */ + --surface: rgb(192, 192, 192); + --button-highlight: rgb(255, 255, 255); + --dialog-blue: rgb(0, 0, 128); + --dialog-blue-light: rgb(16, 132, 208); + --dialog-gray: rgb(128, 128, 128); + --dialog-gray-light: rgb(181, 181, 181); + --link-blue: rgb(0, 0, 128); + + --active-border: rgb(192, 192, 192); + --active-title: var(--dialog-blue); + --app-workspace: rgb(128, 128, 128); + --background: rgb(0, 128, 128); + --button-alternate-face: rgb(192, 192, 192); + --button-dk-shadow: rgb(0, 0, 0); + --button-face: var(--surface); + --button-hilight: var(--button-highlight); + --button-light: rgb(223, 223, 223); + --button-shadow: rgb(128, 128, 128); + --button-text: rgb(0, 0, 0); + --gradient-active-title: var(--dialog-blue-light); + --gradient-inactive-title: var(--dialog-gray-light); + --gray-text: rgb(128, 128, 128); + --hilight: rgb(0, 0, 128); + --hilight-text: rgb(255, 255, 255); + --hot-tracking-color: var(--link-blue); + --inactive-border: rgb(192, 192, 192); + --inactive-title: var(--dialog-gray); + --inactive-title-text: rgb(192, 192, 192); + --info-text: rgb(0, 0, 0); + --info-window: rgb(255, 255, 225); + --menu: rgb(192, 192, 192); + --menu-bar: rgb(192, 192, 192); + --menu-hilight: rgb(0, 0, 128); + --menu-text: rgb(0, 0, 0); + --scrollbar: rgb(192, 192, 192); + --title-text: rgb(255, 255, 255); + --window: rgb(255, 255, 255); + --window-frame: rgb(0, 0, 0); + --window-text: rgb(0, 0, 0); /* Spacing */ --element-spacing: 8px; @@ -49,25 +79,25 @@ /* Borders */ --border-width: 1px; - --border-raised-outer: inset -1px -1px var(--window-frame), - inset 1px 1px var(--button-highlight); + --border-raised-outer: inset -1px -1px var(--button-dk-shadow), + inset 1px 1px var(--button-hilight); --border-raised-inner: inset -2px -2px var(--button-shadow), - inset 2px 2px var(--button-face); - --border-sunken-outer: inset -1px -1px var(--button-highlight), - inset 1px 1px var(--window-frame); - --border-sunken-inner: inset -2px -2px var(--button-face), + inset 2px 2px var(--button-light); + --border-sunken-outer: inset -1px -1px var(--button-hilight), + inset 1px 1px var(--button-dk-shadow); + --border-sunken-inner: inset -2px -2px var(--button-light), inset 2px 2px var(--button-shadow); - /* Window borders flip button-face and button-highlight */ - --border-window-outer: inset -1px -1px var(--window-frame), - inset 1px 1px var(--button-face); + /* Window borders flip button-light and button-hilight */ + --border-window-outer: inset -1px -1px var(--button-dk-shadow), + inset 1px 1px var(--button-light); --border-window-inner: inset -2px -2px var(--button-shadow), - inset 2px 2px var(--button-highlight); + inset 2px 2px var(--button-hilight); - /* Field borders (checkbox, input, etc) flip window-frame and button-shadow */ - --border-field: inset -1px -1px var(--button-highlight), - inset 1px 1px var(--button-shadow), inset -2px -2px var(--button-face), - inset 2px 2px var(--window-frame); + /* Field borders (checkbox, input, etc) flip button-dk-shadow and button-shadow */ + --border-field: inset -1px -1px var(--button-hilight), + inset 1px 1px var(--button-shadow), inset -2px -2px var(--button-light), + inset 2px 2px var(--button-dk-shadow); } @font-face { @@ -89,7 +119,7 @@ body { font-family: Arial; font-size: 12px; - color: #222222; + color: var(--button-text); } button, @@ -124,13 +154,14 @@ h4 { u { text-decoration: none; - border-bottom: 0.5px solid #222222; + border-bottom: 0.5px solid var(--button-text); } button { box-sizing: border-box; border: none; - background: var(--surface); + color: var(--button-text); + background: var(--button-face); box-shadow: var(--border-raised-outer), var(--border-raised-inner); border-radius: 0; @@ -142,7 +173,7 @@ button { .vertical-bar { width: 4px; height: 20px; - background: #c0c0c0; + background: var(--button-face); box-shadow: var(--border-raised-outer), var(--border-raised-inner); } @@ -158,7 +189,7 @@ button:not(:disabled):active { } button:focus { - outline: 1px dotted #000000; + outline: 1px dotted var(--button-text); outline-offset: -4px; } @@ -173,20 +204,21 @@ button::-moz-focus-inner { button:disabled, :disabled + label { - text-shadow: 1px 1px 0 var(--button-highlight); + text-shadow: 1px 1px 0 var(--button-hilight); } .window { box-shadow: var(--border-window-outer), var(--border-window-inner); - background: var(--surface); + color: var(--button-text); + background: var(--button-face); padding: 3px; } .title-bar { background: linear-gradient( 90deg, - var(--dialog-blue), - var(--dialog-blue-light) + var(--active-title), + var(--gradient-active-title) ); padding: 3px 2px 3px 3px; display: flex; @@ -197,14 +229,14 @@ button:disabled, .title-bar.inactive { background: linear-gradient( 90deg, - var(--dialog-gray), - var(--dialog-gray-light) + var(--inactive-title), + var(--gradient-inactive-title) ); } .title-bar-text { font-weight: bold; - color: white; + color: var(--title-text); letter-spacing: 0; margin-right: 24px; } @@ -265,15 +297,15 @@ button:disabled, fieldset { border: none; - box-shadow: inset -1px -1px var(--button-highlight), inset -2px 1px var(--button-shadow), - inset 1px -2px var(--button-shadow), inset 2px 2px var(--button-highlight); + box-shadow: inset -1px -1px var(--button-hilight), inset -2px 1px var(--button-shadow), + inset 1px -2px var(--button-shadow), inset 2px 2px var(--button-hilight); padding: calc(2 * var(--border-width) + var(--element-spacing)); padding-block-start: var(--element-spacing); margin: 0; } legend { - background: var(--surface); + background: var(--button-face); } .field-row { @@ -354,7 +386,7 @@ input[type="radio"]:checked + label::after { input[type="radio"]:focus + label, input[type="checkbox"]:focus + label { - outline: 1px dotted #000000; + outline: 1px dotted var(--button-text); } input[type="radio"][disabled] + label::before { @@ -377,13 +409,13 @@ input[type="checkbox"] + label::before { display: inline-block; width: var(--checkbox-width); height: var(--checkbox-width); - background: var(--button-highlight); + background: var(--window); box-shadow: var(--border-field); margin-right: var(--radio-label-spacing); } input[type="checkbox"]:active + label::before { - background: var(--surface); + background: var(--button-face); } input[type="checkbox"]:checked + label::after { @@ -400,7 +432,7 @@ input[type="checkbox"]:checked + label::after { } input[type="checkbox"][disabled] + label::before { - background: var(--surface); + background: var(--button-face); } input[type="checkbox"][disabled]:checked + label::after { @@ -415,7 +447,8 @@ textarea { padding: 3px 4px; border: none; box-shadow: var(--border-field); - background-color: var(--button-highlight); + color: var(--window-text); + background-color: var(--window); box-sizing: border-box; -webkit-appearance: none; -moz-appearance: none; @@ -441,7 +474,7 @@ input[type="email"]:disabled, input[type="password"]:disabled, input[type="text"]:disabled, textarea:disabled { - background-color: var(--surface); + background-color: var(--button-face); } select { @@ -505,22 +538,36 @@ input[type="range"]::-webkit-slider-runnable-track { width: 100%; height: 2px; box-sizing: border-box; - background: black; - border-right: 1px solid grey; - border-bottom: 1px solid grey; - box-shadow: 1px 0 0 white, 1px 1px 0 white, 0 1px 0 white, -1px 0 0 darkgrey, - -1px -1px 0 darkgrey, 0 -1px 0 darkgrey, -1px 1px 0 white, 1px -1px darkgrey; + background: var(--button-dk-shadow); + border-right: 1px solid var(--button-light); + border-bottom: 1px solid var(--button-light); + box-shadow: + 1px 0 0 var(--button-hilight), + 1px 1px 0 var(--button-hilight), + 0 1px 0 var(--button-hilight), + -1px 0 0 var(--button-shadow), + -1px -1px 0 var(--button-shadow), + 0 -1px 0 var(--button-shadow), + -1px 1px 0 var(--button-hilight), + 1px -1px var(--button-shadow); } input[type="range"]::-moz-range-track { width: 100%; height: 2px; box-sizing: border-box; - background: black; - border-right: 1px solid grey; - border-bottom: 1px solid grey; - box-shadow: 1px 0 0 white, 1px 1px 0 white, 0 1px 0 white, -1px 0 0 darkgrey, - -1px -1px 0 darkgrey, 0 -1px 0 darkgrey, -1px 1px 0 white, 1px -1px darkgrey; + background: var(--button-dk-shadow); + border-right: 1px solid var(--button-light); + border-bottom: 1px solid var(--button-light); + box-shadow: + 1px 0 0 var(--button-hilight), + 1px 1px 0 var(--button-hilight), + 0 1px 0 var(--button-hilight), + -1px 0 0 var(--button-shadow), + -1px -1px 0 var(--button-shadow), + 0 -1px 0 var(--button-shadow), + -1px 1px 0 var(--button-hilight), + 1px -1px var(--button-shadow); } .is-vertical { @@ -540,19 +587,33 @@ input[type="range"]::-moz-range-track { } .is-vertical > input[type="range"]::-webkit-slider-runnable-track { - border-left: 1px solid grey; + border-left: 1px solid var(--button-light); border-right: 0; - border-bottom: 1px solid grey; - box-shadow: -1px 0 0 white, -1px 1px 0 white, 0 1px 0 white, 1px 0 0 darkgrey, - 1px -1px 0 darkgrey, 0 -1px 0 darkgrey, 1px 1px 0 white, -1px -1px darkgrey; + border-bottom: 1px solid var(--button-light); + box-shadow: + -1px 0 0 var(--button-hilight), + -1px 1px 0 var(--button-hilight), + 0 1px 0 var(--button-hilight), + 1px 0 0 var(--button-shadow), + 1px -1px 0 var(--button-shadow), + 0 -1px 0 var(--button-shadow), + 1px 1px 0 var(--button-hilight), + -1px -1px var(--button-shadow); } .is-vertical > input[type="range"]::-moz-range-track { - border-left: 1px solid grey; + border-left: 1px solid var(--button-light); border-right: 0; - border-bottom: 1px solid grey; - box-shadow: -1px 0 0 white, -1px 1px 0 white, 0 1px 0 white, 1px 0 0 darkgrey, - 1px -1px 0 darkgrey, 0 -1px 0 darkgrey, 1px 1px 0 white, -1px -1px darkgrey; + border-bottom: 1px solid var(--button-light); + box-shadow: + -1px 0 0 var(--button-hilight), + -1px 1px 0 var(--button-hilight), + 0 1px 0 var(--button-hilight), + 1px 0 0 var(--button-shadow), + 1px -1px 0 var(--button-shadow), + 0 -1px 0 var(--button-shadow), + 1px 1px 0 var(--button-hilight), + -1px -1px var(--button-shadow); } .is-vertical > input[type="range"]::-webkit-slider-thumb { @@ -572,12 +633,12 @@ input[type="range"]::-moz-range-track { } select:focus { - color: var(--button-highlight); - background-color: var(--dialog-blue); + color: var(--hilight-text); + background-color: var(--hilight); } select:focus option { - color: #000; - background-color: #fff; + color: var(--window-text); + background-color: var(--window); } select:active { @@ -585,16 +646,17 @@ select:active { } a { - color: var(--link-blue); + color: var(--hot-tracking-color); } a:focus { - outline: 1px dotted var(--link-blue); + outline: 1px dotted var(--hot-tracking-color); } ul.tree-view { display: block; - background: var(--button-highlight); + color: var(--window-text); + background: var(--window); box-shadow: var(--border-field); padding: 6px; margin: 0; @@ -606,12 +668,12 @@ ul.tree-view li { ul.tree-view a { text-decoration: none; - color: #000; + color: var(--window-text); } ul.tree-view a:focus { - background-color: var(--dialog-blue); - color: var(--button-highlight); + background-color: var(--hilight); + color: var(--hilight-text); } ul.tree-view ul, @@ -623,7 +685,7 @@ ul.tree-view ul { margin-left: 16px; padding-left: 16px; /* Goes down too far */ - border-left: 1px dotted #808080; + border-left: 1px dotted var(--gray-text); } ul.tree-view ul > li { @@ -636,7 +698,7 @@ ul.tree-view ul > li::before { left: -16px; top: 6px; width: 12px; - border-bottom: 1px dotted #808080; + border-bottom: 1px dotted var(--gray-text); } /* Cover the bottom of the left dotted border */ @@ -648,7 +710,7 @@ ul.tree-view ul > li:last-child::after { top: 7px; bottom: 0px; width: 8px; - background: var(--button-highlight); + background: var(--window); } ul.tree-view details { @@ -670,13 +732,13 @@ ul.tree-view details > summary:before { display: block; float: left; content: "+"; - border: 1px solid #808080; + border: 1px solid var(--gray-text); width: 8px; height: 9px; line-height: 8px; margin-right: 5px; padding-left: 1px; - background-color: #fff; + background-color: var(--window); } ul.tree-view details[open] > summary:before { @@ -685,7 +747,8 @@ ul.tree-view details[open] > summary:before { pre { display: block; - background: var(--button-highlight); + color: var(--window-text); + background: var(--window); box-shadow: var(--border-field); padding: 12px 8px; margin: 0; @@ -697,7 +760,7 @@ code * { } summary:focus { - outline: 1px dotted #000000; + outline: 1px dotted var(--button-text); } ::-webkit-scrollbar { @@ -708,7 +771,7 @@ summary:focus { } ::-webkit-scrollbar-corner { - background: var(--button-face); + background: var(--button-light); } ::-webkit-scrollbar-track { @@ -716,7 +779,7 @@ summary:focus { } ::-webkit-scrollbar-thumb { - background-color: var(--button-face); + background-color: var(--button-light); box-shadow: var(--border-raised-outer), var(--border-raised-inner); } From 119b834aec51b3b63dc59f2952850b5182b4e982 Mon Sep 17 00:00:00 2001 From: tPenguinLTG Date: Wed, 22 Apr 2020 21:34:25 -0400 Subject: [PATCH 2/4] Fix positioning of radio buttons and checkboxes Remove the radio width from the calculation for the radio dot top value. I'm not sure why this works, but it does. Also remove redundant precalc variables. Nested calc calls are reduced to a single one automatically. --- style.css | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/style.css b/style.css index 95ee92c..a25ca2f 100644 --- a/style.css +++ b/style.css @@ -58,21 +58,14 @@ --range-spacing: 10px; /* Some detailed computations for radio buttons and checkboxes */ - --radio-total-width-precalc: var(--radio-width) + var(--radio-label-spacing); - --radio-total-width: calc(var(--radio-total-width-precalc)); - --radio-left: calc(-1 * var(--radio-total-width-precalc)); + --radio-total-width: calc(var(--radio-width) + var(--radio-label-spacing)); + --radio-left: calc(-1 * var(--radio-total-width)); --radio-dot-width: 4px; - --radio-dot-top: calc(var(--radio-width) / 2 - var(--radio-dot-width) / 2); - --radio-dot-left: calc( - -1 * (var(--radio-total-width-precalc)) + var(--radio-width) / 2 - var( - --radio-dot-width - ) / 2 - ); + --radio-dot-top: var(--radio-dot-width); + --radio-dot-left: calc(var(--radio-left) + var(--radio-dot-top)); - --checkbox-total-width-precalc: var(--checkbox-width) + - var(--radio-label-spacing); - --checkbox-total-width: calc(var(--checkbox-total-width-precalc)); - --checkbox-left: calc(-1 * var(--checkbox-total-width-precalc)); + --checkbox-total-width: calc(var(--checkbox-width) + var(--radio-label-spacing)); + --checkbox-left: calc(-1 * var(--checkbox-total-width)); --checkmark-width: 7px; --checkmark-top: 3px; --checkmark-left: 3px; @@ -361,7 +354,7 @@ input[type="radio"] + label::before { content: ""; position: absolute; top: 0; - left: calc(-1 * (var(--radio-total-width-precalc))); + left: var(--radio-left); display: inline-block; width: var(--radio-width); height: var(--radio-width); @@ -405,7 +398,7 @@ input[type="checkbox"] + label { input[type="checkbox"] + label::before { content: ""; position: absolute; - left: calc(-1 * (var(--checkbox-total-width-precalc))); + left: var(--checkbox-left); display: inline-block; width: var(--checkbox-width); height: var(--checkbox-width); @@ -425,9 +418,7 @@ input[type="checkbox"]:checked + label::after { height: var(--checkmark-width); position: absolute; top: var(--checkmark-top); - left: calc( - -1 * (var(--checkbox-total-width-precalc)) + var(--checkmark-left) - ); + left: calc(var(--checkbox-left) + var(--checkmark-left)); background: svg-load("./icon/checkmark.svg"); } From cc99fbd9bbbc737981ef861dd671e07ec3cd1b22 Mon Sep 17 00:00:00 2001 From: tPenguinLTG Date: Mon, 18 May 2020 18:48:08 -0400 Subject: [PATCH 3/4] Enable docs to be themed --- docs/docs.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/docs.css b/docs/docs.css index 74793c9..d56627e 100644 --- a/docs/docs.css +++ b/docs/docs.css @@ -2,6 +2,7 @@ body { margin: 0; padding: 0; background: #c0c0c0; + background: var(--button-face, #c0c0c0); } main { @@ -80,6 +81,7 @@ blockquote { margin: 0 0 20px; padding: 20px; background: #dfdfdf; + background: var(--button-light, #dfdfdf); } blockquote footer { @@ -90,6 +92,7 @@ blockquote footer { margin: 16px 0; padding: 12px 24px; border-left: 1px solid #808080; + border-left: 1px solid var(--button-shadow, #808080); } details { @@ -112,12 +115,15 @@ details[open] summary { button.focused { outline: 1px dotted #000000; + outline: 1px dotted var(--button-text, #000000); outline-offset: -4px; } button.active { box-shadow: inset -1px -1px #ffffff, inset 1px 1px #0a0a0a, inset -2px -2px #dfdfdf, inset 2px 2px #808080; + box-shadow: inset -1px -1px var(--button-hilight, #ffffff), inset 1px 1px var(--button-shadow, #0a0a0a), + inset -2px -2px var(--button-light, #dfdfdf), inset 2px 2px var(--button-dk-shadow, #808080); } @media (max-width: 480px) { From 86b6f52b617e320d9873379c6bdd472ab5693fd6 Mon Sep 17 00:00:00 2001 From: tPenguinLTG Date: Mon, 18 May 2020 19:30:37 -0400 Subject: [PATCH 4/4] Generate computed and non-computed assets The computed asset, 98.css, precomputes the values that uses CSS variables. The non-computed asset, 98-full.css, preserves the variables and uses the computed values as fallbacks for browsers without support for custom properties. --- build.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/build.js b/build.js index 3e4bbf0..78030d6 100644 --- a/build.js +++ b/build.js @@ -10,25 +10,27 @@ const postcss = require("postcss"); const { homepage, version } = require("./package.json"); -function buildCSS() { +function buildCSS(preserveVariables) { const input = `/*! 98.css v${version} - ${homepage} */\n` + fs.readFileSync("style.css"); + const destination = preserveVariables ? "dist/98-full.css" : "dist/98.css"; + const template = preserveVariables ? "[name]-full.[ext]" : "[name].[ext]"; return postcss() .use(require("postcss-inline-svg")) - .use(require("postcss-css-variables")) + .use(require("postcss-css-variables")({ preserve: preserveVariables })) .use(require("postcss-calc")) - .use(require("postcss-copy")({ dest: "dist", template: "[name].[ext]" })) + .use(require("postcss-copy")({ dest: "dist", template })) .use(require("cssnano")) .process(input, { from: "style.css", - to: "dist/98.css", + to: destination, map: { inline: false }, }) .then((result) => { mkdirp.sync("dist"); - fs.writeFileSync("dist/98.css", result.css); - fs.writeFileSync("dist/98.css.map", result.map.toString()); + fs.writeFileSync(destination, result.css); + fs.writeFileSync(destination + ".map", result.map.toString()); }); } @@ -72,7 +74,8 @@ function buildDocs() { } function build() { - buildCSS() + buildCSS(false) + .then(() => buildCSS(true)) .then(buildDocs) .catch((err) => console.log(err)); }