Create the TextMate theme programatically
- ID
694b1f4- date
2025-11-27 04:49:08+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
aad50ee- message
Create the TextMate theme programatically- changed files
15 files, 548 additions, 301 deletions
Changed files
.github/workflows/test.yml (758) → .github/workflows/test.yml (739)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f5d1a61..375477b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -38,7 +38,7 @@ jobs:
ruff format --check .
- name: Check types
- run: mypy scripts/*.py --strict
+ run: mypy . --strict
- name: Run tests
- run: pytest scripts
+ run: pytest
README.md (16) → README.md (349)
diff --git a/README.md b/README.md
index d984164..2efe4bc 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,19 @@
# colour-scheme
+
+## Steps
+
+1. Get a new copy of the CSS files from the `alexwlchan.net` repo:
+
+ ```console
+ $ python3 vendor_css_files.py
+ ```
+
+2. Generate a new set of theme files based on those colours:
+
+ ```console
+ $ python3 generate_palette_files.py
+ ```
+
+## TextMate
+
+Copy the generated theme files into the Bundle Editor.
TextMate.tmTheme (5729) → TextMate.tmTheme (0)
diff --git a/TextMate.tmTheme b/TextMate.tmTheme
deleted file mode 100644
index f410b33..0000000
--- a/TextMate.tmTheme
+++ /dev/null
@@ -1,264 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>author</key>
- <string>Alex Chan</string>
- <key>name</key>
- <string>alexwlchan</string>
- <key>semanticClass</key>
- <string>theme.alexwlchan</string>
- <key>settings</key>
- <array>
- <dict>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#F7F7F7</string>
- <key>caret</key>
- <string>#000000</string>
- <key>foreground</key>
- <string>#000000</string>
- <key>invisibles</key>
- <string>#999999</string>
- <key>lineHighlight</key>
- <string>#FFEB12B3</string>
- <key>selection</key>
- <string>#FFEB12B3</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Text base</string>
- <key>scope</key>
- <string>text</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#F7F7F7</string>
- <key>foreground</key>
- <string>#000000</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Source base</string>
- <key>scope</key>
- <string>source - source source</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#F7F7F7</string>
- <key>foreground</key>
- <string>#000000</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Embedded source (text)</string>
- <key>scope</key>
- <string>text meta.embedded</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#F7F7F7</string>
- <key>foreground</key>
- <string>#000000</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Embedded source (source)</string>
- <key>scope</key>
- <string>source meta.embedded</string>
- <key>settings</key>
- <dict>
- <key>background</key>
- <string>#F7F7F7</string>
- <key>foreground</key>
- <string>#000000</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Comment</string>
- <key>scope</key>
- <string>comment</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#d01c11</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Constant</string>
- <key>scope</key>
- <string>constant</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#3387CC</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Function name</string>
- <key>scope</key>
- <string>entity.name.function</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#115bda</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Variable</string>
- <key>scope</key>
- <string>variable</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#115bda</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Block comment</string>
- <key>scope</key>
- <string>source comment.block</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#d01c11</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>String</string>
- <key>scope</key>
- <string>string</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#1c9611</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>String escapes</string>
- <key>scope</key>
- <string>string constant.character.escape</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#1c9611</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>String (executed)</string>
- <key>scope</key>
- <string>string.interpolated</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#1c9611</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Regular expression</string>
- <key>scope</key>
- <string>string.regexp</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#CCCC33</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>String (literal)</string>
- <key>scope</key>
- <string>string.literal</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#1bad0e</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>String escapes (executed)</string>
- <key>scope</key>
- <string>string.interpolated constant.character.escape</string>
- <key>settings</key>
- <dict>
- <key>foreground</key>
- <string>#1bad0e</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Type name</string>
- <key>scope</key>
- <string>entity.name.type</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string>underline</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Class inheritance</string>
- <key>scope</key>
- <string>entity.other.inherited-class</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string>italic underline</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Tag name</string>
- <key>scope</key>
- <string>entity.name.tag</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string>underline</string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Tag attribute</string>
- <key>scope</key>
- <string>entity.other.attribute-name</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- </dict>
- </dict>
- <dict>
- <key>name</key>
- <string>Support function</string>
- <key>scope</key>
- <string>support.function</string>
- <key>settings</key>
- <dict>
- <key>fontStyle</key>
- <string></string>
- <key>foreground</key>
- <string>#C83730</string>
- </dict>
- </dict>
- </array>
- <key>uuid</key>
- <string>37F22BDC-B2F4-11D9-850C-000A95A89C98</string>
-</dict>
-</plist>
dev_requirements.in (17) → dev_requirements.in (38)
diff --git a/dev_requirements.in b/dev_requirements.in
index daf7d81..ca2402a 100644
--- a/dev_requirements.in
+++ b/dev_requirements.in
@@ -1,3 +1,5 @@
+-r requirements.txt
+
mypy
pytest
ruff
dev_requirements.txt (520) → dev_requirements.txt (635)
diff --git a/dev_requirements.txt b/dev_requirements.txt
index f2b9f6c..14e7a93 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -2,6 +2,12 @@
# uv pip compile dev_requirements.in --output-file dev_requirements.txt
iniconfig==2.3.0
# via pytest
+jinja2==3.1.6
+ # via -r requirements.txt
+markupsafe==3.0.3
+ # via
+ # -r requirements.txt
+ # jinja2
mypy==1.18.2
# via -r dev_requirements.in
mypy-extensions==1.1.0
generate_palette_files.py (0) → generate_palette_files.py (4006)
diff --git a/generate_palette_files.py b/generate_palette_files.py
new file mode 100755
index 0000000..f371a6b
--- /dev/null
+++ b/generate_palette_files.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+
+from datetime import datetime, timezone
+import json
+from pathlib import Path
+import textwrap
+
+from jinja2 import Template
+
+from palette import Colours, Palette, enrich_colours
+
+
+def get_palette() -> tuple[str, Palette]:
+ """
+ Read the palette colours from `palette.json`.
+ """
+ with open("palette.json") as in_file:
+ data = json.load(in_file)
+
+ return data["id"], {
+ "light": enrich_colours(data["light"]),
+ "dark": enrich_colours(data["dark"]),
+ }
+
+
+def generate_textmate_theme(colours: Colours, palette_id: str) -> str:
+ """
+ Generate a TextMate theme based on my palette.
+ """
+ template = Template("""
+ // Generated from palette {{palette_id}} at {{now}}
+ // See https://github.com/alexwlchan/colour-scheme
+ {\tsettings = (
+ \t\t{%- for block in settings %}
+ \t\t{\t{% for k, v in block.items() -%}
+ \t\t\t\t{{ k }} = {% if v is mapping %}{
+ \t\t\t\t{%- for kk, vv in v.items() %}
+ \t\t\t\t{{ kk }} = '{{ vv }}';{% endfor %}
+ \t\t\t};{% else %}'{{ v }}';
+ \t\t\t{% endif %}{% endfor %}
+ \t\t},{% endfor %}
+ \t);
+ }
+ """)
+
+ settings = [
+ {
+ "settings": {
+ "foreground": colours["text"],
+ "background": colours["background"],
+ "caret": colours["text"],
+ "invisibles": colours["punctuation"],
+ "selection": colours["highlight"],
+ "lineHighlight": colours["highlight"],
+ }
+ },
+ {
+ "name": "Text base",
+ "scope": "text",
+ "settings": {
+ "foreground": colours["text"],
+ "background": colours["background"],
+ },
+ },
+ {
+ "name": "Source base",
+ "scope": "source - source source",
+ "settings": {
+ "foreground": colours["text"],
+ "background": colours["background"],
+ },
+ },
+ ]
+
+ for name, scope in [
+ ("Text base", "text"),
+ ("Source base", "source - source source"),
+ ("Embedded source (text)", "text meta.embedded"),
+ ("Embedded source (source)", "source meta.embedded"),
+ ]:
+ settings.append(
+ {
+ "name": name,
+ "scope": scope,
+ "settings": {
+ "foreground": colours["text"],
+ "background": colours["background"],
+ },
+ }
+ )
+
+ for scope, colour in [
+ ("comment", colours["comment"]),
+ ("source comment.block", colours["comment"]),
+ ("constant", colours["literal"]),
+ ("entity.name.function", colours["name"]),
+ ("variable", colours["name"]),
+ ("meta.class.ruby", colours["name"]),
+ ("keyword.control.class.ruby", colours["text"]),
+ ("meta.identifier.python", colours["name"]),
+ ("markup.heading.2.markdown", colours["name"]),
+ ("string", colours["string"]),
+ ("string constant.character.escape", colours["string"]),
+ ("string.interpolated", colours["string"]),
+ ("string.literal", colours["string"]),
+ ("string.interpolated constant.character.escape", colours["string"]),
+ ]:
+ settings.append(
+ {"name": scope, "scope": scope, "settings": {"foreground": colour}}
+ )
+
+ out = template.render(
+ settings=settings,
+ palette_id=palette_id,
+ now=datetime.now(tz=timezone.utc).isoformat(),
+ )
+ out = textwrap.dedent(out)
+ out = out.strip()
+ return out
+
+
+if __name__ == "__main__":
+ palette_id, palette = get_palette()
+
+ out_dir = Path("out")
+ out_dir.mkdir(exist_ok=True)
+
+ (out_dir / "TextMate_light.tmTheme").write_text(
+ generate_textmate_theme(colours=palette["light"], palette_id=palette_id)
+ )
+ (out_dir / "TextMate_dark.tmTheme").write_text(
+ generate_textmate_theme(colours=palette["dark"], palette_id=palette_id)
+ )
out/TextMate_dark.tmTheme (0) → out/TextMate_dark.tmTheme (2924)
diff --git a/out/TextMate_dark.tmTheme b/out/TextMate_dark.tmTheme
new file mode 100644
index 0000000..8fdd4f4
--- /dev/null
+++ b/out/TextMate_dark.tmTheme
@@ -0,0 +1,140 @@
+// Generated from palette 2477498-94fa872 at 2025-11-27T04:47:00.156478+00:00
+// See https://github.com/alexwlchan/colour-scheme
+{ settings = (
+ { settings = {
+ foreground = '#c7c7c7';
+ background = '#0d0d0d';
+ caret = '#c7c7c7';
+ invisibles = '#9a9a9a';
+ selection = '#fffc4244';
+ lineHighlight = '#fffc4244';
+ };
+ },
+ { name = 'Text base';
+ scope = 'text';
+ settings = {
+ foreground = '#c7c7c7';
+ background = '#0d0d0d';
+ };
+ },
+ { name = 'Source base';
+ scope = 'source - source source';
+ settings = {
+ foreground = '#c7c7c7';
+ background = '#0d0d0d';
+ };
+ },
+ { name = 'Text base';
+ scope = 'text';
+ settings = {
+ foreground = '#c7c7c7';
+ background = '#0d0d0d';
+ };
+ },
+ { name = 'Source base';
+ scope = 'source - source source';
+ settings = {
+ foreground = '#c7c7c7';
+ background = '#0d0d0d';
+ };
+ },
+ { name = 'Embedded source (text)';
+ scope = 'text meta.embedded';
+ settings = {
+ foreground = '#c7c7c7';
+ background = '#0d0d0d';
+ };
+ },
+ { name = 'Embedded source (source)';
+ scope = 'source meta.embedded';
+ settings = {
+ foreground = '#c7c7c7';
+ background = '#0d0d0d';
+ };
+ },
+ { name = 'comment';
+ scope = 'comment';
+ settings = {
+ foreground = '#f45858';
+ };
+ },
+ { name = 'source comment.block';
+ scope = 'source comment.block';
+ settings = {
+ foreground = '#f45858';
+ };
+ },
+ { name = 'constant';
+ scope = 'constant';
+ settings = {
+ foreground = '#ff42fc';
+ };
+ },
+ { name = 'entity.name.function';
+ scope = 'entity.name.function';
+ settings = {
+ foreground = '#40c3ff';
+ };
+ },
+ { name = 'variable';
+ scope = 'variable';
+ settings = {
+ foreground = '#40c3ff';
+ };
+ },
+ { name = 'meta.class.ruby';
+ scope = 'meta.class.ruby';
+ settings = {
+ foreground = '#40c3ff';
+ };
+ },
+ { name = 'keyword.control.class.ruby';
+ scope = 'keyword.control.class.ruby';
+ settings = {
+ foreground = '#c7c7c7';
+ };
+ },
+ { name = 'meta.identifier.python';
+ scope = 'meta.identifier.python';
+ settings = {
+ foreground = '#40c3ff';
+ };
+ },
+ { name = 'markup.heading.2.markdown';
+ scope = 'markup.heading.2.markdown';
+ settings = {
+ foreground = '#40c3ff';
+ };
+ },
+ { name = 'string';
+ scope = 'string';
+ settings = {
+ foreground = '#5ff042';
+ };
+ },
+ { name = 'string constant.character.escape';
+ scope = 'string constant.character.escape';
+ settings = {
+ foreground = '#5ff042';
+ };
+ },
+ { name = 'string.interpolated';
+ scope = 'string.interpolated';
+ settings = {
+ foreground = '#5ff042';
+ };
+ },
+ { name = 'string.literal';
+ scope = 'string.literal';
+ settings = {
+ foreground = '#5ff042';
+ };
+ },
+ { name = 'string.interpolated constant.character.escape';
+ scope = 'string.interpolated constant.character.escape';
+ settings = {
+ foreground = '#5ff042';
+ };
+ },
+ );
+}
\ No newline at end of file
out/TextMate_light.tmTheme (0) → out/TextMate_light.tmTheme (2924)
diff --git a/out/TextMate_light.tmTheme b/out/TextMate_light.tmTheme
new file mode 100644
index 0000000..ecb214b
--- /dev/null
+++ b/out/TextMate_light.tmTheme
@@ -0,0 +1,140 @@
+// Generated from palette 2477498-94fa872 at 2025-11-27T04:47:00.154723+00:00
+// See https://github.com/alexwlchan/colour-scheme
+{ settings = (
+ { settings = {
+ foreground = '#202020';
+ background = '#fafafa';
+ caret = '#202020';
+ invisibles = '#999999';
+ selection = '#ffeb12b3';
+ lineHighlight = '#ffeb12b3';
+ };
+ },
+ { name = 'Text base';
+ scope = 'text';
+ settings = {
+ foreground = '#202020';
+ background = '#fafafa';
+ };
+ },
+ { name = 'Source base';
+ scope = 'source - source source';
+ settings = {
+ foreground = '#202020';
+ background = '#fafafa';
+ };
+ },
+ { name = 'Text base';
+ scope = 'text';
+ settings = {
+ foreground = '#202020';
+ background = '#fafafa';
+ };
+ },
+ { name = 'Source base';
+ scope = 'source - source source';
+ settings = {
+ foreground = '#202020';
+ background = '#fafafa';
+ };
+ },
+ { name = 'Embedded source (text)';
+ scope = 'text meta.embedded';
+ settings = {
+ foreground = '#202020';
+ background = '#fafafa';
+ };
+ },
+ { name = 'Embedded source (source)';
+ scope = 'source meta.embedded';
+ settings = {
+ foreground = '#202020';
+ background = '#fafafa';
+ };
+ },
+ { name = 'comment';
+ scope = 'comment';
+ settings = {
+ foreground = '#d01c11';
+ };
+ },
+ { name = 'source comment.block';
+ scope = 'source comment.block';
+ settings = {
+ foreground = '#d01c11';
+ };
+ },
+ { name = 'constant';
+ scope = 'constant';
+ settings = {
+ foreground = '#c311d0';
+ };
+ },
+ { name = 'entity.name.function';
+ scope = 'entity.name.function';
+ settings = {
+ foreground = '#115bda';
+ };
+ },
+ { name = 'variable';
+ scope = 'variable';
+ settings = {
+ foreground = '#115bda';
+ };
+ },
+ { name = 'meta.class.ruby';
+ scope = 'meta.class.ruby';
+ settings = {
+ foreground = '#115bda';
+ };
+ },
+ { name = 'keyword.control.class.ruby';
+ scope = 'keyword.control.class.ruby';
+ settings = {
+ foreground = '#202020';
+ };
+ },
+ { name = 'meta.identifier.python';
+ scope = 'meta.identifier.python';
+ settings = {
+ foreground = '#115bda';
+ };
+ },
+ { name = 'markup.heading.2.markdown';
+ scope = 'markup.heading.2.markdown';
+ settings = {
+ foreground = '#115bda';
+ };
+ },
+ { name = 'string';
+ scope = 'string';
+ settings = {
+ foreground = '#1bad0e';
+ };
+ },
+ { name = 'string constant.character.escape';
+ scope = 'string constant.character.escape';
+ settings = {
+ foreground = '#1bad0e';
+ };
+ },
+ { name = 'string.interpolated';
+ scope = 'string.interpolated';
+ settings = {
+ foreground = '#1bad0e';
+ };
+ },
+ { name = 'string.literal';
+ scope = 'string.literal';
+ settings = {
+ foreground = '#1bad0e';
+ };
+ },
+ { name = 'string.interpolated constant.character.escape';
+ scope = 'string.interpolated constant.character.escape';
+ settings = {
+ foreground = '#1bad0e';
+ };
+ },
+ );
+}
\ No newline at end of file
palette.json (362) → palette.json (526)
diff --git a/palette.json b/palette.json
index 6b7693e..e5c17cf 100644
--- a/palette.json
+++ b/palette.json
@@ -1,6 +1,9 @@
{
"id": "2477498-94fa872",
"light": {
+ "background": "#fafafa",
+ "text": "#202020",
+ "accent_grey": "#999999",
"red": "#d01c11",
"green": "#1bad0e",
"blue": "#115bda",
@@ -9,11 +12,14 @@
"highlight": "#ffeb12b3"
},
"dark": {
+ "background": "#0d0d0d",
+ "text": "#c7c7c7",
+ "accent_grey": "#9a9a9a",
"red": "#f45858",
"green": "#5ff042",
"blue": "#40c3ff",
"magenta": "#ff42fc",
"yellow": "#fffc42",
- "highlight": "#fffc42cc"
+ "highlight": "#fffc4244"
}
}
\ No newline at end of file
palette.py (0) → palette.py (861)
diff --git a/palette.py b/palette.py
new file mode 100644
index 0000000..3f9ceff
--- /dev/null
+++ b/palette.py
@@ -0,0 +1,48 @@
+from typing import TypedDict
+
+
+class BaseColours(TypedDict):
+ background: str
+ text: str
+ accent_grey: str
+ red: str
+ green: str
+ blue: str
+ magenta: str
+ yellow: str
+ highlight: str
+
+
+class Colours(TypedDict):
+ background: str
+ text: str
+ comment: str
+ literal: str
+ string: str
+ name: str
+ punctuation: str
+ highlight: str
+
+
+def enrich_colours(c: BaseColours) -> Colours:
+ return {
+ "background": c["background"],
+ "text": c["text"],
+ "comment": c["red"],
+ "literal": c["magenta"],
+ "string": c["green"],
+ "name": c["blue"],
+ "punctuation": c["accent_grey"],
+ "highlight": c["highlight"],
+ }
+
+
+class BasePalette(TypedDict):
+ id: str
+ light: BaseColours
+ dark: BaseColours
+
+
+class Palette(TypedDict):
+ light: Colours
+ dark: Colours
requirements.in (0) → requirements.in (7)
diff --git a/requirements.in b/requirements.in
new file mode 100644
index 0000000..7f7afbf
--- /dev/null
+++ b/requirements.in
@@ -0,0 +1 @@
+jinja2
requirements.txt (0) → requirements.txt (208)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..398b0bf
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+# This file was autogenerated by uv via the following command:
+# uv pip compile requirements.in --output-file requirements.txt
+jinja2==3.1.6
+ # via -r requirements.in
+markupsafe==3.0.3
+ # via jinja2
scripts/palette.py (228) → scripts/palette.py (0)
diff --git a/scripts/palette.py b/scripts/palette.py
deleted file mode 100644
index 7985b7c..0000000
--- a/scripts/palette.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import TypedDict
-
-
-class Colours(TypedDict):
- red: str
- green: str
- blue: str
- magenta: str
- yellow: str
- highlight: str
-
-
-class Palette(TypedDict):
- id: str
- light: Colours
- dark: Colours
scripts/test_vendor_css_files.py (572) → test_vendor_css_files.py (654)
diff --git a/scripts/test_vendor_css_files.py b/test_vendor_css_files.py
similarity index 94%
rename from scripts/test_vendor_css_files.py
rename to test_vendor_css_files.py
index 6f73e54..b99808c 100644
--- a/scripts/test_vendor_css_files.py
+++ b/test_vendor_css_files.py
@@ -12,6 +12,8 @@ from vendor_css_files import get_colour_variable
("--red: #ff0000;", "red", "#ff0000"),
# Alpha channel
("--red: #ff0000ff;", "red", "#ff0000ff"),
+ # Three-digit hex in source
+ ("--grey: #999;", "grey", "#999999"),
],
)
def test_get_colour_variable(css: str, name: str, colour: str) -> None:
scripts/vendor_css_files.py (3379) → vendor_css_files.py (4507)
diff --git a/scripts/vendor_css_files.py b/vendor_css_files.py
similarity index 67%
rename from scripts/vendor_css_files.py
rename to vendor_css_files.py
index 5a4669e..6ce7029 100755
--- a/scripts/vendor_css_files.py
+++ b/vendor_css_files.py
@@ -11,7 +11,7 @@ import re
import shutil
import subprocess
-from palette import Colours, Palette
+from palette import BaseColours, BasePalette
def get_alexwlchan_net_css(css_name: str) -> tuple[str, str]:
@@ -55,40 +55,65 @@ def get_colour_variable(css: str, *, name: str) -> str:
# --red: #ff0000;
# --red: #ff0000ff;
#
- m = re.search(f"--{name}:" + r"\s*(?P<colour>#[0-9a-f]{6}([0-9a-f]{2})?);", css)
+ m = re.search(f"{name}:" + r"\s*(?P<colour>#[0-9a-f]+);", css)
if m is None:
raise ValueError(f"cannot find variable --{name} in CSS")
- return m.group("colour")
+ c = m.group("colour")
+
+ # 6- or 8-digit hex colour
+ if len(c) == 7 or len(c) == 9:
+ return c
+
+ # 3-digit hex colour, so double each digit
+ if len(c) == 4:
+ return f"#{c[1] * 2}{c[2] * 2}{c[3] * 2}"
+
+ raise ValueError(f"unrecognised hex string: {c}")
if __name__ == "__main__":
variable_id, variable_css = get_alexwlchan_net_css("variables.scss")
syntax_id, syntax_css = get_alexwlchan_net_css("components/syntax_highlighting.css")
- light_colours: Colours = {
- "red": get_colour_variable(variable_css, name="default-primary-color-light"),
- "green": get_colour_variable(syntax_css, name="green"),
- "blue": get_colour_variable(syntax_css, name="blue"),
- "magenta": get_colour_variable(syntax_css, name="magenta"),
- "yellow": get_colour_variable(syntax_css, name="yellow"),
- "highlight": get_colour_variable(syntax_css, name="highlight"),
+ light_colours: BaseColours = {
+ "background": get_colour_variable(
+ variable_css, name="--background-color-light"
+ ),
+ "text": get_colour_variable(variable_css, name="--body-text-light"),
+ "accent_grey": get_colour_variable(variable_css, name="--accent-grey-light"),
+ "red": get_colour_variable(variable_css, name="--default-primary-color-light"),
+ "green": get_colour_variable(syntax_css, name="--green"),
+ "blue": get_colour_variable(syntax_css, name="--blue"),
+ "magenta": get_colour_variable(syntax_css, name="--magenta"),
+ "yellow": get_colour_variable(syntax_css, name="--yellow"),
+ "highlight": get_colour_variable(syntax_css, name="--highlight"),
}
# Get the first block of dark theme colours from the syntax highlighting
# CSS. This is a bit crude, but it works for now.
_, dark_syntax_css = syntax_css.split("@media (prefers-color-scheme: dark) {")
- dark_colours: Colours = {
- "red": get_colour_variable(variable_css, name="default-primary-color-dark"),
- "green": get_colour_variable(dark_syntax_css, name="green"),
- "blue": get_colour_variable(dark_syntax_css, name="blue"),
- "magenta": get_colour_variable(dark_syntax_css, name="magenta"),
- "yellow": get_colour_variable(dark_syntax_css, name="yellow"),
- "highlight": get_colour_variable(dark_syntax_css, name="highlight"),
+ dark_colours: BaseColours = {
+ "background": get_colour_variable(variable_css, name="--background-color-dark"),
+ "text": get_colour_variable(variable_css, name="--body-text-dark"),
+ "accent_grey": get_colour_variable(variable_css, name="--accent-grey-dark"),
+ "red": get_colour_variable(variable_css, name="--default-primary-color-dark"),
+ "green": get_colour_variable(dark_syntax_css, name="--green"),
+ "blue": get_colour_variable(dark_syntax_css, name="--blue"),
+ "magenta": get_colour_variable(dark_syntax_css, name="--magenta"),
+ "yellow": get_colour_variable(dark_syntax_css, name="--yellow"),
+ "highlight": get_colour_variable(dark_syntax_css, name="--highlight"),
}
- palette: Palette = {
+ # When I do <mark> highlights on my blog, I keep the text black in
+ # dark mode, but for my themes, use a more muted yellow.
+ if dark_colours["highlight"] == "#fffc42cc":
+ dark_colours["highlight"] = "#fffc4244"
+ else:
+ raise ValueError(f"Unrecognised dark colour: {dark_colours['highlight']}")
+
+ palette: BasePalette = {
"id": f"{variable_id}-{syntax_id}",
"light": light_colours,
"dark": dark_colours,