searxng: add Nord theme and deploy script
All checks were successful
CI / update (push) Successful in 3m25s

Override SearXNG's native CSS variables with Nord palette (cream white
light mode, true black dark mode). Replace SearXNG logo with Bocken
logo. Custom base.html template injects the CSS. Deploy script supports
reset to restore original state.
This commit is contained in:
2026-03-08 20:33:25 +01:00
parent 42754204b2
commit f76b647918
3 changed files with 421 additions and 0 deletions

53
deploy-searxng.sh Executable file
View File

@@ -0,0 +1,53 @@
#!/bin/sh
# Deploy SearXNG custom theme to searx.bocken.org
# CSS is hosted on bocken.org, template override on the SearXNG server
#
# Usage:
# ./deploy-searxng.sh Deploy custom theme
# ./deploy-searxng.sh reset Restore original SearXNG base.html and remove custom CSS
CSS_SRC=static/other/searxng.css
CSS_DEST=/var/www/static/css/searxng.css
TMPL_SRC=static/other/searxng_base.html
TMPL_DEST=/var/lib/searxng/venv/lib/python3.14/site-packages/searx/templates/simple/base.html
TMPL_BACKUP="${TMPL_DEST}.orig"
if [ "$1" = "reset" ]; then
echo "Resetting SearXNG to original theme..."
ssh root@bocken.org "
if [ -f '$TMPL_BACKUP' ]; then
mv '$TMPL_BACKUP' '$TMPL_DEST'
chown searxng:searxng '$TMPL_DEST'
else
echo 'No backup found at $TMPL_BACKUP — nothing to restore'
exit 1
fi
rm -f '$CSS_DEST'
systemctl restart uwsgi@emperor
"
echo "Done. Original theme restored."
exit 0
fi
# Back up original base.html if no backup exists yet
ssh root@bocken.org "
if [ ! -f '$TMPL_BACKUP' ]; then
cp '$TMPL_DEST' '$TMPL_BACKUP'
echo 'Backed up original base.html'
fi
"
# Deploy CSS to bocken.org static hosting
ssh root@bocken.org "mkdir -p /var/www/static/css"
rsync -av "$CSS_SRC" "root@bocken.org:$CSS_DEST"
# Deploy custom base.html template to SearXNG server
rsync -av "$TMPL_SRC" "root@bocken.org:$TMPL_DEST"
ssh root@bocken.org "chown searxng:searxng '$TMPL_DEST'"
# Restart SearXNG to pick up template changes
ssh root@bocken.org "systemctl restart uwsgi@emperor"
echo "Done. Check https://searx.bocken.org"
echo "To restore original: ./deploy-searxng.sh reset"

276
static/other/searxng.css Normal file
View File

@@ -0,0 +1,276 @@
/*
SearXNG custom theme for searx.bocken.org
Overrides SearXNG's native CSS variables with Nord palette
Deployed via deploy-searxng.sh
*/
/* ============================================
LIGHT MODE — cream white base
============================================ */
:root {
--color-base-font: #2a2a2a;
--color-base-font-rgb: 42, 42, 42;
--color-base-background: #f8f6f1;
--color-base-background-mobile: #efecea;
--color-url-font: #5E81AC;
--color-url-visited-font: #B48EAD;
--color-header-background: #f8f6f1;
--color-header-border: #dfdcd8;
--color-footer-background: #f8f6f1;
--color-footer-border: #dfdcd8;
--color-sidebar-border: #dfdcd8;
--color-sidebar-font: #2a2a2a;
--color-sidebar-background: #efecea;
--color-backtotop-font: #555;
--color-backtotop-border: #dfdcd8;
--color-backtotop-background: #efecea;
--color-btn-background: #5E81AC;
--color-btn-font: #fff;
--color-show-btn-background: #dfdcd8;
--color-show-btn-font: #2a2a2a;
--color-search-border: #dfdcd8;
--color-search-shadow: 0 2px 8px rgba(0,0,0,0.08);
--color-search-background: #efecea;
--color-search-font: #2a2a2a;
--color-search-background-hover: #5E81AC;
--color-error: #BF616A;
--color-error-background: #f5e1e3;
--color-warning: #EBCB8B;
--color-warning-background: #faf5e1;
--color-success: #A3BE8C;
--color-success-background: #e8f2e1;
--color-categories-item-selected-font: #5E81AC;
--color-categories-item-border-selected: #5E81AC;
--color-autocomplete-font: #2a2a2a;
--color-autocomplete-border: #dfdcd8;
--color-autocomplete-shadow: 0 2px 8px rgba(0,0,0,0.08);
--color-autocomplete-background: #efecea;
--color-autocomplete-background-hover: #e8e5e1;
--color-answer-font: #2a2a2a;
--color-answer-background: #efecea;
--color-result-keyvalue-col-table: #f8f6f1;
--color-result-keyvalue-odd: #f8f6f1;
--color-result-keyvalue-even: #efecea;
--color-result-background: #efecea;
--color-result-border: #dfdcd8;
--color-result-url-font: #555;
--color-result-vim-selected: #e8e5e1cc;
--color-result-vim-arrow: #5E81AC;
--color-result-description-highlight-font: #2a2a2a;
--color-result-link-font: #5E81AC;
--color-result-link-font-highlight: #5E81AC;
--color-result-link-visited-font: #B48EAD;
--color-result-publishdate-font: #777;
--color-result-engines-font: #777;
--color-result-search-url-border: #dfdcd8;
--color-result-search-url-font: #555;
--color-result-image-span-font: #555;
--color-result-image-span-font-selected: #fff;
--color-result-image-background: #efecea;
--color-settings-tr-hover: #e8e5e1;
--color-settings-engine-description-font: #777;
--color-settings-table-group-background: rgba(0,0,0,0.03);
--color-result-detail-font: #fff;
--color-result-detail-label-font: #D8DEE9;
--color-result-detail-background: #2E3440;
--color-result-detail-hr: #4C566A;
--color-result-detail-link: #88C0D0;
--color-result-detail-loader-border: rgba(255,255,255,0.2);
--color-result-detail-loader-borderleft: transparent;
--color-toolkit-badge-font: #fff;
--color-toolkit-badge-background: #4C566A;
--color-toolkit-kbd-font: #fff;
--color-toolkit-kbd-background: #2E3440;
--color-toolkit-dialog-border: #dfdcd8;
--color-toolkit-dialog-background: #f8f6f1;
--color-toolkit-tabs-label-border: #f8f6f1;
--color-toolkit-tabs-section-border: #dfdcd8;
--color-toolkit-select-background: #e8e5e1;
--color-toolkit-select-border: #dfdcd8;
--color-toolkit-select-background-hover: #dfdcd8;
--color-toolkit-input-text-font: #2a2a2a;
--color-toolkit-checkbox-onoff-off-background: #dfdcd8;
--color-toolkit-checkbox-onoff-on-background: #dfdcd8;
--color-toolkit-checkbox-onoff-on-mark-background: #5E81AC;
--color-toolkit-checkbox-onoff-on-mark-color: #fff;
--color-toolkit-checkbox-onoff-off-mark-background: #aaa;
--color-toolkit-checkbox-onoff-off-mark-color: #fff;
--color-toolkit-checkbox-label-background: #dfdcd8;
--color-toolkit-checkbox-label-border: #dfdcd8;
--color-toolkit-checkbox-input-border: #5E81AC;
--color-toolkit-engine-tooltip-border: #dfdcd8;
--color-toolkit-engine-tooltip-background: #f8f6f1;
--color-toolkit-loader-border: rgba(0,0,0,0.1);
--color-toolkit-loader-borderleft: transparent;
--color-doc-code: #2E3440;
--color-doc-code-background: #e8e5e1;
--color-bar-chart-primary: #5E81AC;
--color-bar-chart-secondary: #D08770;
--color-image-resolution-background: rgba(0,0,0,0.5);
--color-image-resolution-font: #fff;
--color-loading-indicator: rgba(255,255,255,0.2);
--color-loading-indicator-gap: #f8f6f1;
--color-line-number: #4C566A;
--color-favicon-background-color: #e8e5e1;
--color-favicon-border-color: #dfdcd8;
}
/* ============================================
DARK MODE — true black base
============================================ */
@media (prefers-color-scheme: dark) {
:root.theme-auto {
--color-base-font: #e5e5e5;
--color-base-font-rgb: 229, 229, 229;
--color-base-background: #000;
--color-base-background-mobile: #000;
--color-url-font: #88C0D0;
--color-url-visited-font: #c89fb6;
--color-header-background: #000;
--color-header-border: #222;
--color-footer-background: #000;
--color-footer-border: #222;
--color-sidebar-border: #333;
--color-sidebar-font: #e5e5e5;
--color-sidebar-background: #1a1a1a;
--color-backtotop-font: #aaa;
--color-backtotop-border: #333;
--color-backtotop-background: #1a1a1a;
--color-btn-background: #88C0D0;
--color-btn-font: #000;
--color-show-btn-background: #333;
--color-show-btn-font: #e5e5e5;
--color-search-border: #333;
--color-search-shadow: 0 2px 8px rgba(0,0,0,0.5);
--color-search-background: #1a1a1a;
--color-search-font: #e5e5e5;
--color-search-background-hover: #88C0D0;
--color-error: #BF616A;
--color-error-background: #2a0f0f;
--color-warning: #EBCB8B;
--color-warning-background: #2a2008;
--color-success: #A3BE8C;
--color-success-background: #0e2a0a;
--color-categories-item-selected-font: #88C0D0;
--color-categories-item-border-selected: #88C0D0;
--color-autocomplete-font: #e5e5e5;
--color-autocomplete-border: #333;
--color-autocomplete-shadow: 0 2px 8px rgba(0,0,0,0.5);
--color-autocomplete-background: #1a1a1a;
--color-autocomplete-background-hover: #111;
--color-answer-font: #e5e5e5;
--color-answer-background: #1a1a1a;
--color-result-keyvalue-col-table: #111;
--color-result-keyvalue-odd: #111;
--color-result-keyvalue-even: #1a1a1a;
--color-result-background: #1a1a1a;
--color-result-border: #222;
--color-result-url-font: #888;
--color-result-vim-selected: #111c;
--color-result-vim-arrow: #88C0D0;
--color-result-description-highlight-font: #fff;
--color-result-link-font: #88C0D0;
--color-result-link-font-highlight: #88C0D0;
--color-result-link-visited-font: #c89fb6;
--color-result-publishdate-font: #888;
--color-result-engines-font: #888;
--color-result-search-url-border: #333;
--color-result-search-url-font: #aaa;
--color-result-image-span-font: #aaa;
--color-result-image-span-font-selected: #fff;
--color-result-image-background: #1a1a1a;
--color-settings-tr-hover: #222;
--color-settings-engine-description-font: #888;
--color-settings-table-group-background: rgba(255,255,255,0.03);
--color-result-detail-font: #e5e5e5;
--color-result-detail-label-font: #aaa;
--color-result-detail-background: #111;
--color-result-detail-hr: #333;
--color-result-detail-link: #88C0D0;
--color-result-detail-loader-border: rgba(255,255,255,0.2);
--color-result-detail-loader-borderleft: transparent;
--color-toolkit-badge-font: #e5e5e5;
--color-toolkit-badge-background: #4C566A;
--color-toolkit-kbd-font: #e5e5e5;
--color-toolkit-kbd-background: #000;
--color-toolkit-dialog-border: #333;
--color-toolkit-dialog-background: #111;
--color-toolkit-tabs-label-border: #111;
--color-toolkit-tabs-section-border: #333;
--color-toolkit-select-background: #222;
--color-toolkit-select-border: #333;
--color-toolkit-select-background-hover: #333;
--color-toolkit-input-text-font: #e5e5e5;
--color-toolkit-checkbox-onoff-off-background: #333;
--color-toolkit-checkbox-onoff-on-background: #333;
--color-toolkit-checkbox-onoff-on-mark-background: #88C0D0;
--color-toolkit-checkbox-onoff-on-mark-color: #000;
--color-toolkit-checkbox-onoff-off-mark-background: #555;
--color-toolkit-checkbox-onoff-off-mark-color: #e5e5e5;
--color-toolkit-checkbox-label-background: #333;
--color-toolkit-checkbox-label-border: #333;
--color-toolkit-checkbox-input-border: #88C0D0;
--color-toolkit-engine-tooltip-border: #333;
--color-toolkit-engine-tooltip-background: #111;
--color-toolkit-loader-border: rgba(255,255,255,0.1);
--color-toolkit-loader-borderleft: transparent;
--color-doc-code: #e5e5e5;
--color-doc-code-background: #1a1a1a;
--color-bar-chart-primary: #88C0D0;
--color-bar-chart-secondary: #D08770;
--color-image-resolution-background: rgba(0,0,0,0.7);
--color-image-resolution-font: #e5e5e5;
--color-loading-indicator: rgba(255,255,255,0.2);
--color-loading-indicator-gap: #000;
--color-line-number: #4C566A;
--color-favicon-background-color: #222;
--color-favicon-border-color: #333;
}
}
/* ============================================
FONT
============================================ */
* {
font-family: Helvetica, Arial, "Noto Sans", sans-serif !important;
}
/* ============================================
INDEX PAGE — LOGO
Replace SearXNG logo with Bocken logo
============================================ */
.index .title {
background-image: url("https://bocken.org/static/css/logos/logo_text_smart.svg") !important;
}
/* Results page logo */
#search_logo svg,
#search_logo img {
display: none !important;
}
#search_logo span {
display: none !important;
}
#search_logo::after {
content: "";
display: block;
width: 100px;
height: 30px;
background: url("https://bocken.org/static/css/logos/logo_full_light.svg") no-repeat center / contain;
}
@media (prefers-color-scheme: light) {
#search_logo::after {
background-image: url("https://bocken.org/static/css/logos/logo_full_dark.svg");
}
}
/* ============================================
SCROLLBAR
============================================ */
* {
scrollbar-width: thin;
scrollbar-color: var(--color-header-border) var(--color-base-background);
}

View File

@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html class="no-js theme-{{ preferences.get_value('simple_style') or 'auto' }} center-alignment-{{ preferences.get_value('center_alignment') and 'yes' or 'no' }}" lang="{{ locale_rfc5646 }}" {% if rtl %} dir="rtl"{% endif %}>
<head>
<meta charset="UTF-8">
<meta name="endpoint" content="{{ endpoint }}">
<meta name="description" content="SearXNG — a privacy-respecting, open metasearch engine">
<meta name="keywords" content="SearXNG, search, search engine, metasearch, meta search">
<meta name="generator" content="searxng/{{ searx_version }}">
<meta name="referrer" content="no-referrer">
<meta name="robots" content="noarchive">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{% endblock %}{{ instance_name }}</title>
<script type="module" src="{{ url_for('static', filename='sxng-core.min.js') }}" client_settings="{{ client_settings
}}"></script>
{% block meta %}{% endblock %}
{% if rtl %}
<link rel="stylesheet" href="{{ url_for('static', filename='sxng-rtl.min.css') }}" type="text/css" media="screen">
{% else %}
<link rel="stylesheet" href="{{ url_for('static', filename='sxng-ltr.min.css') }}" type="text/css" media="screen">
{% endif %}
{% if get_setting('server.limiter') or get_setting('server.public_instance') %}
<link rel="stylesheet" href="{{ url_for('client_token', token=link_token) }}" type="text/css">
{% endif %}
<!-- bocken.org custom theme -->
<link rel="stylesheet" href="https://bocken.org/static/css/searxng.css" type="text/css" media="screen">
{% block head %}
<link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ opensearch_url }}">
{% endblock %}
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}" sizes="any">
<link rel="icon" href="{{ url_for('static', filename='img/favicon.svg') }}" type="image/svg+xml">
<link rel="apple-touch-icon" href="{{ url_for('static', filename='img/favicon.png') }}">
</head>
<body class="{{ endpoint }}_endpoint" >
<main id="main_{{ self._TemplateReference__context.name|replace("simple/", "")|replace(".html", "") }}" class="{{body_class}}">
{% if errors %}
<div class="dialog-error" role="alert">
<a href="#" class="close" aria-label="close" title="close">×</a>
<ul>
{% for message in errors %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<nav id="links_on_top">
{%- from 'simple/icons.html' import icon_big -%}
{%- block linkto_about -%}
<a href="{{ url_for('info', pagename='about') }}" class="link_on_top_about">{{ icon_big('information-circle') }}<span>{{ _('About') }}</span></a>
{%- endblock -%}
{%- block linkto_donate -%}
{%- if donation_url -%}
<a href="{{ donation_url }}" class="link_on_top_donate">{{ icon_big('heart') }}<span>{{ _('Donate') }}</span></a>
{%- endif -%}
{%- endblock -%}
{%- block linkto_preferences -%}
{%- if request.args.get('preferences') -%}
<a href="{{ url_for('preferences') }}?preferences={{ request.args.get('preferences') }}&preferences_preview_only=true" class="link_on_top_preferences">{{ icon_big('settings') }}<span>{{ _('Preferences') }}</span></a>
{%- else -%}
<a href="{{ url_for('preferences') }}" class="link_on_top_preferences">{{ icon_big('settings') }}<span>{{ _('Preferences') }}</span></a>
{%- endif -%}
{%- endblock -%}
</nav>
{% block header %}
{% endblock %}
{% block content %}
{% endblock %}
</main>
<footer>
<p>
{{ _('Powered by') }} <a href="{{ url_for('info', pagename='about') }}">SearXNG</a> - {{ searx_version }} — {{ _('a privacy-respecting, open metasearch engine') }}<br>
<a href="{{ searx_git_url }}">{{ _('Source code') }}</a>
| <a href="{{ get_setting('brand.issue_url') }}">{{ _('Issue tracker') }}</a>
{% if enable_metrics %}| <a href="{{ url_for('stats') }}">{{ _('Engine stats') }}</a>{% endif %}
{% if get_setting('brand.public_instances') %}
| <a href="{{ get_setting('brand.public_instances') }}">{{ _('Public instances') }}</a>
{% endif %}
{% if get_setting('general.privacypolicy_url') %}
| <a href="{{ get_setting('general.privacypolicy_url') }}">{{ _('Privacy policy') }}</a>
{% endif %}
{% if get_setting('general.contact_url') %}
| <a href="{{ get_setting('general.contact_url') }}">{{ _('Contact instance maintainer') }}</a>
{% endif %}
{% for title, link in get_setting('brand.custom.links').items() %}
| <a href="{{ link }}">{{ _(title) }}</a>
{% endfor %}
</p>
</footer>
</body>
</html>