From cca6f519bd34e370af395e2f5fabfd9c7e1f13e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?T=C3=B3th=20Patrik?=
Date: Wed, 29 Jul 2026 13:52:30 +0200
Subject: [PATCH] theme support
---
build.js | 34 +++-
docs/docs.css | 4 +-
docs/index.html.ejs | 79 ++++++++
icon/button-down-active.svg | 2 +-
icon/button-down.svg | 2 +-
icon/button-left.svg | 2 +-
icon/button-right.svg | 2 +-
icon/button-up.svg | 2 +-
icon/indicator-horizontal.svg | 2 +-
icon/indicator-rectangle-horizontal.svg | 2 +-
icon/radio-border-active.svg | 7 +
icon/scrollbar-background.svg | 2 +-
server.js | 2 +-
style.css | 74 ++++---
themes.css | 255 ++++++++++++++++++++++++
15 files changed, 430 insertions(+), 41 deletions(-)
create mode 100644 icon/radio-border-active.svg
create mode 100644 themes.css
diff --git a/build.js b/build.js
index 3e4bbf0..fb73e6c 100644
--- a/build.js
+++ b/build.js
@@ -12,11 +12,11 @@ const { homepage, version } = require("./package.json");
function buildCSS() {
const input =
- `/*! 98.css v${version} - ${homepage} */\n` + fs.readFileSync("style.css");
+ `/*! 98.css v${version} - ${homepage} */\n` + fs.readFileSync("themes.css") + fs.readFileSync("style.css");
return postcss()
.use(require("postcss-inline-svg"))
- .use(require("postcss-css-variables"))
+ //.use(require("postcss-css-variables"))
.use(require("postcss-calc"))
.use(require("postcss-copy")({ dest: "dist", template: "[name].[ext]" }))
.use(require("cssnano"))
@@ -71,9 +71,39 @@ function buildDocs() {
);
}
+function generateThemeManifest() {
+ const cssPath = "themes.css";
+ const outputPath = "dist/themes.json";
+ const css = fs.readFileSync(cssPath, "utf8");
+
+ // Match top-level selectors like ".theme-brick {"
+ const themeRegex = /\.theme-([\w-]+)\s*\{/g;
+
+ const themes = [];
+ let match;
+
+ while ((match = themeRegex.exec(css)) !== null) {
+ const slug = match[1]; // e.g. "brick"
+ themes.push({
+ name: slug
+ .split("-")
+ .map(w => w.charAt(0).toUpperCase() + w.slice(1))
+ .join(" "), // "brick" -> "Brick", "dark-blue" -> "Dark Blue"
+ class: `theme-${slug}`,
+ });
+ }
+
+ fs.writeFileSync(outputPath, JSON.stringify(themes, null, 2));
+ console.log(`Wrote ${themes.length} themes to ${outputPath}`);
+
+ return themes;
+}
+
+
function build() {
buildCSS()
.then(buildDocs)
+ .then(generateThemeManifest)
.catch((err) => console.log(err));
}
module.exports = build;
diff --git a/docs/docs.css b/docs/docs.css
index a38ce5b..e1e95b6 100644
--- a/docs/docs.css
+++ b/docs/docs.css
@@ -1,7 +1,7 @@
body {
margin: 0;
padding: 0;
- background: #c0c0c0;
+ background: var(--surface);
}
main {
@@ -79,7 +79,7 @@ p {
blockquote {
margin: 0 0 20px;
padding: 20px;
- background: #dfdfdf;
+ background: rgba(255, 255, 255, 0.4);
}
blockquote footer {
diff --git a/docs/index.html.ejs b/docs/index.html.ejs
index be5a9a8..4855aa6 100644
--- a/docs/index.html.ejs
+++ b/docs/index.html.ejs
@@ -22,6 +22,13 @@
npm install 98.css
+ Themes
+
+
+ Bundled
+
+
+ Some of the default Windows themes are bundled. To use them, simply
+ add the theme's class to your body or html
+ element.
+
+
+
+
+
+
+
+
+
+
+ Custom
+
+
+ You can easily make custom themes or modify the colors runtime using
+ JavaScript. All parameters are available as CSS variables.
+
+
+
.theme-rose {
+ --font-family-title-bar: "Times New Roman", Times, serif;
+ --font-size-title-bar: 13px;
+ --text-color: #000000;
+ --text-color-inactive-title-bar: #606068;
+ --background-color: #808080;
+ --surface: #cfafb7;
+ --button-highlight: #ffffff;
+ --button-face: var(--surface);
+ --button-shadow: #808080;
+ --window-frame: #0a0a0a;
+ --dialog-blue: #9f6070;
+ --dialog-blue-light: #9f6070;
+ --dialog-gray: #a0a0a4;
+ --dialog-gray-light: #a0a0a4;
+ --link-blue: #0000ff;
+}
+
+
+
+
Components
@@ -1125,5 +1181,28 @@
subscribing to more fun things on my twitter. 👋
+
+