diff --git a/.github/actions/setup-python/action.yml b/.github/actions/setup-python/action.yml index f451497..d1c358a 100644 --- a/.github/actions/setup-python/action.yml +++ b/.github/actions/setup-python/action.yml @@ -4,7 +4,7 @@ runs: using: "composite" steps: - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version-file: "pyproject.toml" - name: Install uv diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e52f25a..b8f8db7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,11 +2,17 @@ name: checks on: pull_request: workflow_dispatch: + +permissions: + contents: read + jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - uses: ./.github/actions/setup-python - run: | uv run ruff check --fix @@ -15,13 +21,17 @@ jobs: typecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - uses: ./.github/actions/setup-python - run: uv run ty check vulture: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - uses: ./.github/actions/setup-python - run: uv run vulture --min-confidence 100 --exclude ".venv" . diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 73ddf28..d49809e 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -10,11 +10,24 @@ jobs: pytest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - uses: ./.github/actions/setup-python - run: | uv run pytest + # Browser/Playwright tests are excluded by default (pytest.ini) and run + # only on pull requests, where we install Chromium for them. + - name: Install Playwright browser + if: github.event_name == 'pull_request' + run: | + uv run playwright install --with-deps chromium + - name: Run browser tests + if: github.event_name == 'pull_request' + run: | + uv run pytest -m browser + - name: Run tests (with coverage) run: | uv run pytest \ @@ -24,7 +37,7 @@ jobs: | tee pytest-coverage.txt - name: Pytest coverage comment - uses: MishaKav/pytest-coverage-comment@main + uses: MishaKav/pytest-coverage-comment@dda025d84b1dd831193ed9cd5b1d7e33c79efa9b # v1.1.59 with: pytest-coverage-path: ./pytest-coverage.txt junitxml-path: ./pytest.xml diff --git a/docs/schema/cheatsheet.schema.json b/docs/schema/cheatsheet.schema.json index e255089..a988c01 100644 --- a/docs/schema/cheatsheet.schema.json +++ b/docs/schema/cheatsheet.schema.json @@ -22,6 +22,20 @@ "default": false, "description": "When true, free-form shortcut text is allowed; requires RenderKeys=false." }, + "theme": { + "type": "string", + "minLength": 1, + "default": "catppuccin", + "description": "Theme name: a built-in (e.g. 'catppuccin') or a user theme in the project's themes/ directory. Defaults to 'catppuccin'." + }, + "custom_css": { + "type": "string", + "description": "Filename of a custom CSS file (resolved from the project's styles/ directory), layered after the theme." + }, + "custom_css_inline": { + "type": "string", + "description": "Inline custom CSS, emitted last in the cascade so it overrides the theme and the custom CSS file." + }, "layout": { "type": "object", "additionalProperties": false, diff --git a/pyproject.toml b/pyproject.toml index 74a8320..e323fc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ dev = [ "prek>=0.3.6", "pytest>=8.4.2", "pytest-cov>=7.1.0", + "pytest-playwright>=0.7.1", "ruff>=0.14.0", "ty>=0.0.1a22", "vulture>=2.14", diff --git a/pytest.ini b/pytest.ini index edcbd06..19f1050 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,9 +3,12 @@ testpaths = tests python_files = test_*.py python_classes = Test* python_functions = test_* -addopts = +addopts = -v --tb=short --strict-markers --disable-warnings + -m "not browser" +markers = + browser: browser-based Playwright tests diff --git a/src/koalakeys/generate_cheatsheet.py b/src/koalakeys/generate_cheatsheet.py index 0aeb394..3c56305 100644 --- a/src/koalakeys/generate_cheatsheet.py +++ b/src/koalakeys/generate_cheatsheet.py @@ -10,6 +10,7 @@ from koalakeys.logger import get_logger from koalakeys.template_renderer import render_template +from koalakeys.theming import ThemeError, resolve_theme, sanitize_css from koalakeys.validate_yaml import lint_yaml, validate_yaml yaml_safe = YAML(typ="safe") @@ -25,6 +26,8 @@ OUTPUT_DIR = Path(os.getenv("CHEATSHEET_OUTPUT_DIR") or PROJECT_ROOT / "output") CHEATSHEETS_DIR = PROJECT_ROOT / "cheatsheets" +THEMES_DIR = PROJECT_ROOT / "themes" +STYLES_DIR = PROJECT_ROOT / "styles" LAYOUTS_DIR = PACKAGE_DIR / "layouts" OUTPUT_DIR.mkdir(exist_ok=True, parents=True) @@ -135,6 +138,41 @@ def get_layout_info(data): } +def load_custom_css_file(filename): + if not filename: + return "" + path = STYLES_DIR / filename + try: + return sanitize_css(path.read_text(encoding="utf-8")) + except FileNotFoundError: + logging.error(f"Custom CSS file not found: {path}") + return "" + except OSError as e: + logging.error(f"Error reading custom CSS file '{path}': {e}") + return "" + + +def apply_styling(data): + """Resolve the cheatsheet's theme and custom CSS into render-ready context. + + Returns True on success; False if the theme cannot be resolved. + """ + try: + theme = resolve_theme(data.get("theme"), themes_dir=THEMES_DIR) + except ThemeError as e: + logging.error(f"Theme error: {e}") + return False + + data["theme_token_css"] = theme.render_token_css() + data["theme_font_url"] = theme.font_url + data["theme_default_is_dark"] = theme.default_is_dark + data["theme_both_modes"] = theme.both_modes + data["theme_custom_css"] = theme.custom_css + data["custom_css_file"] = load_custom_css_file(data.get("custom_css")) + data["custom_css_inline"] = sanitize_css(str(data.get("custom_css_inline") or "")) + return True + + def generate_html(data, keyboard_layouts, system_mappings): template_path = "cheatsheets/cheatsheet-template.html" layout_info = get_layout_info(data) @@ -144,6 +182,9 @@ def generate_html(data, keyboard_layouts, system_mappings): data["render_keys"] = data.get("RenderKeys", True) data["allow_text"] = data.get("AllowText", False) + if not apply_styling(data): + return None + return render_template(template_path, data) diff --git a/src/koalakeys/templates/base.html b/src/koalakeys/templates/base.html index 26c756c..3f2b25b 100644 --- a/src/koalakeys/templates/base.html +++ b/src/koalakeys/templates/base.html @@ -4,11 +4,11 @@ - + {% block fonts %}{% endblock %} {% block title %}{% endblock %} {% block page_styles %}{% endblock %} - + {% block content %}{% endblock %} {% block javascript %}{% endblock %} diff --git a/src/koalakeys/templates/cheatsheets/assets/cheatsheets.css b/src/koalakeys/templates/cheatsheets/assets/cheatsheets.css index 6a6d792..d3c5aa9 100644 --- a/src/koalakeys/templates/cheatsheets/assets/cheatsheets.css +++ b/src/koalakeys/templates/cheatsheets/assets/cheatsheets.css @@ -1,89 +1,23 @@ -/* Catppuccin Latte (Light) Theme */ -:root { - --latte-rosewater: #dc8a78; - --latte-flamingo: #dd7878; - --latte-pink: #ea76cb; - --latte-mauve: #8839ef; - --latte-red: #d20f39; - --latte-maroon: #e64553; - --latte-peach: #fe640b; - --latte-yellow: #df8e1d; - --latte-green: #40a02b; - --latte-teal: #179299; - --latte-sky: #04a5e5; - --latte-sapphire: #209fb5; - --latte-blue: #1e66f5; - --latte-lavender: #7287fd; - --latte-text: #4c4f69; - --latte-subtext1: #5c5f77; - --latte-subtext0: #6c6f85; - --latte-overlay2: #7c7f93; - --latte-overlay1: #8c8fa1; - --latte-overlay0: #9ca0b0; - --latte-surface2: #acb0be; - --latte-surface1: #bcc0cc; - --latte-surface0: #ccd0da; - --latte-base: #eff1f5; - --latte-mantle: #e6e9ef; - --latte-crust: #dce0e8; -} - -/* Catppuccin Mocha (Dark) Theme */ -.dark-mode { - --mocha-rosewater: #f5e0dc; - --mocha-flamingo: #f2cdcd; - --mocha-pink: #f5c2e7; - --mocha-mauve: #cba6f7; - --mocha-red: #f38ba8; - --mocha-maroon: #eba0ac; - --mocha-peach: #fab387; - --mocha-yellow: #f9e2af; - --mocha-green: #a6e3a1; - --mocha-teal: #94e2d5; - --mocha-sky: #89dceb; - --mocha-sapphire: #74c7ec; - --mocha-blue: #89b4fa; - --mocha-lavender: #b4befe; - --mocha-text: #cdd6f4; - --mocha-subtext1: #bac2de; - --mocha-subtext0: #a6adc8; - --mocha-overlay2: #9399b2; - --mocha-overlay1: #7f849c; - --mocha-overlay0: #6c7086; - --mocha-surface2: #585b70; - --mocha-surface1: #45475a; - --mocha-surface0: #313244; - --mocha-base: #1e1e2e; - --mocha-mantle: #181825; - --mocha-crust: #11111b; -} +/* + * Component stylesheet — consumes only the --kk-* semantic token contract. + * Token VALUES are emitted per-theme by the generator (see koalakeys/themes.py); + * this file never references palette-specific variables or mode overrides. + * Token reference: docs/themes/tokens.md + */ body { - font-family: "Iosevka Web", monospace; - line-height: 1.6; - color: var(--latte-text); + font-family: var(--kk-font); + font-size: var(--kk-font-size); + line-height: var(--kk-line-height); + color: var(--kk-text); max-width: 1400px; margin: 0 auto; padding: 0 10px 20px 90px; - background-color: var(--latte-base); + background-color: var(--kk-bg); transition: background-color 0.3s, color 0.3s; } -body:not(.dark-mode) { - --text: var(--latte-text); - --subtext1: var(--latte-subtext1); - --subtext0: var(--latte-subtext0); - --overlay2: var(--latte-overlay2); - --overlay1: var(--latte-overlay1); - --overlay0: var(--latte-overlay0); - --surface2: var(--latte-surface2); - --surface1: var(--latte-surface1); - --surface0: var(--latte-surface0); - --base: var(--latte-base); - --mantle: var(--latte-mantle); - --crust: var(--latte-crust); -} #content-wrapper { transition: padding-left 0.3s ease; padding-left: 0; @@ -91,12 +25,8 @@ body:not(.dark-mode) { body.nav-active #content-wrapper { padding-left: 200px; } -body.dark-mode { - background-color: var(--mocha-base); - color: var(--mocha-text); -} h1 { - color: var(--latte-mauve); + color: var(--kk-accent); text-align: center; margin-bottom: 10px; font-weight: 700; @@ -112,42 +42,30 @@ h1 { padding: 0; transform: translateX(-50%); } - -body.dark-mode h1 { - color: var(--mocha-mauve); -} .section { - background-color: var(--mantle); - border-radius: 6px; + background-color: var(--kk-surface); + border-radius: var(--kk-radius); padding: 8px; - margin-bottom: 15px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin-bottom: var(--kk-section-gap); + box-shadow: 0 4px 6px var(--kk-shadow); transition: background-color 0.3s, color 0.3s, box-shadow 0.3s; } -body.dark-mode .section { - background-color: var(--mocha-surface0); - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); -} .section h2 { - color: var(--latte-lavender); - border-bottom: 1px solid var(--latte-lavender); + color: var(--kk-accent-2); + border-bottom: 1px solid var(--kk-accent-2); padding-bottom: 5px; margin-top: 0; margin-bottom: 10px; font-weight: 600; font-size: 1.3rem; } -body.dark-mode .section h2 { - color: var(--mocha-lavender); - border-bottom-color: var(--mocha-lavender); -} .shortcuts-container { display: grid; grid-template-columns: repeat(4, 1fr); - gap: 12px; + gap: var(--kk-gap); } @media (max-width: 1200px) { .shortcuts-container { @@ -165,32 +83,24 @@ body.dark-mode .section h2 { } } .shortcut { - background-color: var(--crust); - border-radius: 5px; + background-color: var(--kk-card); + border-radius: var(--kk-radius); cursor: pointer; transition: background-color 0.3s, transform 0.2s, box-shadow 0.2s; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 4px var(--kk-shadow); display: flex; justify-content: space-between; align-items: center; min-height: 40px; overflow: hidden; } -body.dark-mode .shortcut { - background-color: var(--mocha-surface1); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); -} .shortcut:hover { - background-color: var(--surface1); + background-color: var(--kk-card-hover); transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); -} -body.dark-mode .shortcut:hover { - background-color: var(--mocha-surface2); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); + box-shadow: 0 4px 8px var(--kk-shadow-strong); } .shortcut:active { transform: translateY(0); @@ -198,8 +108,8 @@ body.dark-mode .shortcut:hover { .shortcut-key { font-weight: 600; font-size: 0.95rem; - color: var(--latte-mauve); - font-family: "Iosevka Web", monospace; + color: var(--kk-key-text); + font-family: var(--kk-font); transition: color 0.3s, font-size 0.3s; @@ -213,26 +123,15 @@ body.dark-mode .shortcut:hover { .text-shortcut { font-size: 0.95rem; - color: var(--latte-text); -} - -body.dark-mode .text-shortcut { - color: var(--mocha-text); -} -body.dark-mode .shortcut-key { - color: var(--mocha-red); + color: var(--kk-text); } .shortcut-key .key-part { - color: var(--latte-mauve); -} - -body.dark-mode .shortcut-key .key-part { - color: var(--mocha-red); + color: var(--kk-key-text); } .shortcut-key .separator { - color: var(--latte-subtext0); + color: var(--kk-text-muted); font-weight: normal; margin-right: 0.3em; margin-left: 0.3em; @@ -242,10 +141,6 @@ body.dark-mode .shortcut-key .key-part { white-space-collapse: preserve; } -body.dark-mode .shortcut-key .separator { - color: var(--mocha-subtext0); -} - .shortcut-key .modifier-symbol { font-size: 1.5em; display: inline-block; @@ -285,16 +180,12 @@ body.dark-mode .shortcut-key .separator { max-width: 1000px; margin: 0 auto; border-radius: 10px; - background: var(--crust); + background: var(--kk-keyboard-bg); padding: 5px; transition: background-color 0.3s, box-shadow 0.3s; - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); -} -body.dark-mode #keyboard { - background: var(--mocha-crust); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); + box-shadow: 0 6px 12px var(--kk-shadow); } .row { display: flex; @@ -302,11 +193,11 @@ body.dark-mode #keyboard { margin-bottom: 5px; } .key { - width: 50px; - height: 50px; - background: var(--crust); - border: 1px solid var(--overlay0); - border-radius: 4px; + width: var(--kk-key-size); + height: var(--kk-key-size); + background: var(--kk-key-bg); + border: 1px solid var(--kk-border); + border-radius: calc(var(--kk-radius) - 2px); display: flex; justify-content: center; align-items: center; @@ -317,8 +208,8 @@ body.dark-mode #keyboard { color 0.3s, border-color 0.3s, box-shadow 0.3s; - color: var(--text); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + color: var(--kk-text); + box-shadow: 0 2px 4px var(--kk-shadow); cursor: pointer; flex-grow: 1; margin-right: 4px; @@ -326,87 +217,39 @@ body.dark-mode #keyboard { .key:last-child { margin-right: 0; } -body.dark-mode .key { - background: var(--mocha-surface0); - color: var(--mocha-text); - border-color: var(--mocha-overlay0); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); -} -.key.active { - background: var(--latte-green); - color: var(--latte-base); - box-shadow: 0 0 8px var(--latte-green); -} -body.dark-mode .key.active { - background: var(--mocha-green); - color: var(--mocha-base); - box-shadow: 0 0 8px var(--mocha-green); -} +.key.active, .key.active-step-1 { - background: var(--latte-green); - color: var(--latte-base); - box-shadow: 0 0 8px var(--latte-green); -} -body.dark-mode .key.active-step-1 { - background: var(--mocha-green); - color: var(--mocha-base); - box-shadow: 0 0 8px var(--mocha-green); + background: var(--kk-step-1); + color: var(--kk-step-text); + box-shadow: 0 0 8px var(--kk-step-1); } .key.active-step-2 { - background: var(--latte-sapphire); - color: var(--latte-base); - box-shadow: 0 0 8px var(--latte-sapphire); -} -body.dark-mode .key.active-step-2 { - background: var(--mocha-sapphire); - color: var(--mocha-base); - box-shadow: 0 0 8px var(--mocha-sapphire); + background: var(--kk-step-2); + color: var(--kk-step-text); + box-shadow: 0 0 8px var(--kk-step-2); } .key.active-step-3 { - background: var(--latte-peach); - color: var(--latte-base); - box-shadow: 0 0 8px var(--latte-peach); -} -body.dark-mode .key.active-step-3 { - background: var(--mocha-peach); - color: var(--mocha-base); - box-shadow: 0 0 8px var(--mocha-peach); + background: var(--kk-step-3); + color: var(--kk-step-text); + box-shadow: 0 0 8px var(--kk-step-3); } .key.active-step-4 { - background: var(--latte-mauve); - color: var(--latte-base); - box-shadow: 0 0 8px var(--latte-mauve); -} -body.dark-mode .key.active-step-4 { - background: var(--mocha-mauve); - color: var(--mocha-base); - box-shadow: 0 0 8px var(--mocha-mauve); + background: var(--kk-step-4); + color: var(--kk-step-text); + box-shadow: 0 0 8px var(--kk-step-4); } .key.active-step-5 { - background: var(--latte-lavender); - color: var(--latte-base); - box-shadow: 0 0 8px var(--latte-lavender); -} -body.dark-mode .key.active-step-5 { - background: var(--mocha-lavender); - color: var(--mocha-base); - box-shadow: 0 0 8px var(--mocha-lavender); + background: var(--kk-step-5); + color: var(--kk-step-text); + box-shadow: 0 0 8px var(--kk-step-5); } /* Default style for steps greater than 5 */ .key[class*="active-step-"]:not(.active-step-1):not(.active-step-2):not( .active-step-3 ):not(.active-step-4):not(.active-step-5) { - background: var(--latte-yellow); - color: var(--latte-base); - box-shadow: 0 0 8px var(--latte-yellow); -} -body.dark-mode - .key[class*="active-step-"]:not(.active-step-1):not(.active-step-2):not( - .active-step-3 - ):not(.active-step-4):not(.active-step-5) { - background: var(--mocha-yellow); - color: var(--mocha-base); - box-shadow: 0 0 8px var(--mocha-yellow); + background: var(--kk-step-overflow); + color: var(--kk-step-text); + box-shadow: 0 0 8px var(--kk-step-overflow); } .key__wide { width: 75px; @@ -468,10 +311,7 @@ body.dark-mode text-align: center; margin-bottom: 20px; font-size: 1.1rem; - color: var(--latte-subtext1); -} -body.dark-mode .layout-info { - color: var(--mocha-subtext1); + color: var(--kk-text-muted); } .top-content { @@ -500,10 +340,10 @@ body.nav-active .search-container { width: 100%; padding: 10px; font-size: 16px; - border: 2px solid var(--latte-surface2); - border-radius: 5px; - background-color: var(--latte-base); - color: var(--latte-text); + border: 2px solid var(--kk-border-input); + border-radius: var(--kk-radius); + background-color: var(--kk-bg); + color: var(--kk-text); transition: border-color 0.3s, background-color 0.3s, @@ -519,26 +359,15 @@ body.nav-active #shortcut-search { width: calc(100% - 200px); } -body.dark-mode #shortcut-search { - border-color: var(--mocha-surface2); - background-color: var(--mocha-base); - color: var(--mocha-text); -} - #shortcut-search:focus { outline: none; - border-color: var(--latte-lavender); - box-shadow: 0 0 5px var(--latte-lavender); -} - -body.dark-mode #shortcut-search:focus { - border-color: var(--mocha-lavender); - box-shadow: 0 0 5px var(--mocha-lavender); + border-color: var(--kk-accent-2); + box-shadow: 0 0 5px var(--kk-accent-2); } #content-wrapper { padding-top: 10px; - max-width: 1200px; + max-width: var(--kk-max-width); margin-left: auto; margin-right: auto; transition: padding-left 0.3s ease; @@ -556,14 +385,10 @@ body.nav-active #content-wrapper { border: none; cursor: pointer; font-size: 24px; - color: var(--text); + color: var(--kk-text); transition: color 0.3s; } -body.dark-mode #dark-mode-toggle { - color: var(--mocha-text); -} - #category-nav-toggle { flex: 0 0 auto; } @@ -572,7 +397,7 @@ body.dark-mode #dark-mode-toggle { top: 20px; left: 10px; z-index: 1000; - background: var(--latte-surface1); + background: var(--kk-card-hover); border: none; border-radius: 50%; width: 40px; @@ -581,15 +406,12 @@ body.dark-mode #dark-mode-toggle { display: flex; align-items: center; justify-content: center; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 5px var(--kk-shadow-strong); transition: background-color 0.3s, transform 0.2s, left 0.3s; } -body.dark-mode #category-nav-toggle { - background: var(--mocha-surface1); -} #category-nav-toggle:hover { transform: scale(1.1); } @@ -608,32 +430,22 @@ body.dark-mode #category-nav-toggle { left: -200px; width: 200px; height: 100vh; - background: var(--surface0); + background: var(--kk-surface); overflow-y: auto; transition: left 0.3s ease; z-index: 998; padding: 70px 15px 20px; - box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); + box-shadow: 2px 0 5px var(--kk-shadow); } #category-nav.active { left: 0; } -body.dark-mode #category-nav { - background: var(--mocha-surface0); - box-shadow: 2px 0 5px rgba(0, 0, 0, 0.3); -} #category-nav ul { display: none; } #category-nav.active ul { display: block; } -body.dark-mode #category-nav { - background: var(--mocha-surface0); -} -#category-nav.active { - left: 0; -} #category-nav-toggle { z-index: 999; } @@ -646,17 +458,11 @@ body.dark-mode #category-nav { margin-bottom: 10px; } #category-nav a { - color: var(--latte-text); + color: var(--kk-text); text-decoration: none; font-size: 1rem; transition: color 0.2s; } -body.dark-mode #category-nav a { - color: var(--mocha-text); -} #category-nav a:hover { - color: var(--latte-lavender); -} -body.dark-mode #category-nav a:hover { - color: var(--mocha-lavender); + color: var(--kk-accent-2); } diff --git a/src/koalakeys/templates/cheatsheets/cheatsheet-template.html b/src/koalakeys/templates/cheatsheets/cheatsheet-template.html index 4263047..b9aee56 100644 --- a/src/koalakeys/templates/cheatsheets/cheatsheet-template.html +++ b/src/koalakeys/templates/cheatsheets/cheatsheet-template.html @@ -2,9 +2,21 @@ {% block title %}{{ title }} Cheatsheet{% endblock %} +{% block body_class %}{% if theme_default_is_dark %}dark-mode{% endif %}{% endblock %} + +{% block fonts %}{% if theme_font_url %}{% endif %}{% endblock %} + {% block page_styles %} {% endblock %} diff --git a/src/koalakeys/templates/cheatsheets/components/body.html b/src/koalakeys/templates/cheatsheets/components/body.html index db6d7ff..7e1f06f 100644 --- a/src/koalakeys/templates/cheatsheets/components/body.html +++ b/src/koalakeys/templates/cheatsheets/components/body.html @@ -18,11 +18,13 @@

{{ title }}

+ {% if theme_both_modes %} + {% endif %}