diff --git a/librewolf/README.md b/librewolf/README.md new file mode 100644 index 0000000..8cd5a0f --- /dev/null +++ b/librewolf/README.md @@ -0,0 +1,75 @@ +# LibreWolf + +Themes the LibreWolf browser chrome (toolbar, tabs, URL bar, menus, sidebar) and +the built-in `about:` pages with the Noctalia palette, using the same native +`userChrome.css` / `userContent.css` mechanism as Firefox. No extension required. + +LibreWolf is a Firefox fork, so this template is the same CSS as the +[`firefox/`](../firefox/) template, adapted to LibreWolf's profile locations. + +## What it themes + +- **Browser chrome** (`librewolf-userChrome.css`): tab bar, navigation toolbar, + URL bar and its results, bookmarks bar, sidebar, find bar, menus/panels, + context menus, dialogs, autocomplete popups, download panel, select dropdowns. +- **Content pages** (`librewolf-userContent.css`): `about:newtab` / `about:home`, + `about:preferences` and its sub-dialogs, `about:addons`, `about:logins`, + `about:protections`, `about:config`, `about:profiles`, and more. + +Both dark and light variants are generated; the browser switches automatically +via `prefers-color-scheme`. + +## Setup + +When the Noctalia theming script runs, it automatically: + +1. Finds LibreWolf profiles by locating their `prefs.js` files, in: + - `~/.librewolf` (regular installs) + - `~/.config/librewolf` (new XDG layout — profiles may be one level deeper, + e.g. `~/.config/librewolf/librewolf//`) + - `~/.var/app/io.gitlab.librewolf-community/.librewolf` (Flatpak) + - `~/snap/librewolf/common/.librewolf` (Snap) +2. Creates each profile's `chrome/` directory when necessary. +3. Adds the Noctalia imports to `userChrome.css` and `userContent.css`. +4. Enables the required preferences through the profile's `user.js`: + - `toolkit.legacyUserProfileCustomizations.stylesheets` — load `userChrome.css` + - `svg.context-properties.content.enabled` — colored icons on `about:` pages + - `devtools.chrome.enabled` — browser chrome debugging + +No manual changes in `about:config` are required. + +## Set website appearance + +Open **Settings → Appearance** and set **Website Appearance** to **Auto** so +websites and the browser follow the color scheme generated by Noctalia. + +## Apply changes + +**Restart LibreWolf** after the initial setup. A restart may also be required +after color changes for the updated theme to appear. + +## Troubleshooting + +- **Theme not appearing:** make sure the template is enabled in Noctalia, + re-apply your theme, then restart LibreWolf. +- **Some colors look wrong:** extensions or existing `userChrome.css` content + can override template colors. Check `chrome/userChrome.css` for other imports. +- **Only some profiles are themed:** the hook updates every profile it finds + under the paths above. Profiles stored in a custom location are not touched. + +## Removing the theme + +Disable the template in Noctalia, delete the +`@import "…librewolf-userChrome.css";` / `@import "…librewolf-userContent.css";` +lines from each profile's `userChrome.css` / `userContent.css`, and remove the +preferences added by this template from `user.js` (or delete those files if you +never customized them). + +## Files + +| File | Purpose | +| --- | --- | +| `template.toml` | Noctalia manifest (renders both CSS files, runs `apply.sh`) | +| `apply.sh` | Finds profiles, wires up imports and preferences (idempotent) | +| `librewolf-userChrome.css` | Browser chrome theme | +| `librewolf-userContent.css` | `about:` pages theme | diff --git a/librewolf/about-preferences-screenshot.png b/librewolf/about-preferences-screenshot.png new file mode 100644 index 0000000..66fc0be Binary files /dev/null and b/librewolf/about-preferences-screenshot.png differ diff --git a/librewolf/apply.sh b/librewolf/apply.sh new file mode 100755 index 0000000..42bf658 --- /dev/null +++ b/librewolf/apply.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}" +css_chrome="$cache_dir/noctalia/librewolf/librewolf-userChrome.css" +css_content="$cache_dir/noctalia/librewolf/librewolf-userContent.css" +line_chrome="@import \"$css_chrome\";" +line_content="@import \"$css_content\";" + +write_if_changed() { + local target="$1" tmp="$2" + if ! cmp -s "$target" "$tmp"; then + cat "$tmp" >"$target" + fi + rm -f "$tmp" +} + +# LibreWolf keeps one prefs.js per profile, under one of these roots: +# ~/.librewolf (regular installs) +# $XDG_CONFIG_HOME/librewolf (new XDG layout; profiles may +# sit directly under it or one +# level deeper under librewolf/) +# ~/.var/app/io.gitlab.librewolf-community/... (Flatpak) +# ~/snap/librewolf/common/... (Snap) +roots=() +for root in \ + "${HOME}/.librewolf" \ + "${XDG_CONFIG_HOME:-$HOME/.config}/librewolf" \ + "${HOME}/.var/app/io.gitlab.librewolf-community/.librewolf" \ + "${HOME}/snap/librewolf/common/.librewolf" +do + [ -d "$root" ] && roots+=("$root") +done + +if [ "${#roots[@]}" -gt 0 ]; then + find "${roots[@]}" -mindepth 1 -maxdepth 3 -type f -name "prefs.js" -print0 2>/dev/null | + while IFS= read -r -d '' prefs_file; do + profile_dir=$(dirname "$prefs_file") + chrome_dir="$profile_dir/chrome" + user_chrome="$chrome_dir/userChrome.css" + user_content="$chrome_dir/userContent.css" + user_js="$profile_dir/user.js" + + mkdir -p "$chrome_dir" + touch "$user_chrome" "$user_content" "$user_js" + + tmp_chrome="$(mktemp "${user_chrome}.tmp.XXXXXX")" + sed '/noctalia\/librewolf\/librewolf-userChrome\.css/d' "$user_chrome" >"$tmp_chrome" + if ! grep -Fq "$line_chrome" "$tmp_chrome"; then + [ -s "$tmp_chrome" ] && [ -n "$(tail -c1 "$tmp_chrome")" ] && echo >>"$tmp_chrome" + printf '%s\n' "$line_chrome" >>"$tmp_chrome" + fi + write_if_changed "$user_chrome" "$tmp_chrome" + + tmp_content="$(mktemp "${user_content}.tmp.XXXXXX")" + sed '/noctalia\/librewolf\/librewolf-userContent\.css/d' "$user_content" >"$tmp_content" + if ! grep -Fq "$line_content" "$tmp_content"; then + [ -s "$tmp_content" ] && [ -n "$(tail -c1 "$tmp_content")" ] && echo >>"$tmp_content" + printf '%s\n' "$line_content" >>"$tmp_content" + fi + write_if_changed "$user_content" "$tmp_content" + + tmp_js="$(mktemp "${user_js}.tmp.XXXXXX")" + sed \ + -e '/toolkit\.legacyUserProfileCustomizations\.stylesheets/d' \ + -e '/svg\.context-properties\.content\.enabled/d' \ + -e '/devtools\.chrome\.enabled/d' \ + "$user_js" >"$tmp_js" + [ -s "$tmp_js" ] && [ -n "$(tail -c1 "$tmp_js")" ] && echo >>"$tmp_js" + printf '%s\n' \ + 'user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);' \ + 'user_pref("svg.context-properties.content.enabled", true);' \ + 'user_pref("devtools.chrome.enabled", true);' >>"$tmp_js" + write_if_changed "$user_js" "$tmp_js" + done +fi diff --git a/librewolf/librewolf-userChrome.css b/librewolf/librewolf-userChrome.css new file mode 100644 index 0000000..52f7d0c --- /dev/null +++ b/librewolf/librewolf-userChrome.css @@ -0,0 +1,543 @@ +@media (prefers-color-scheme: dark) { + * { + --base: {{colors.background.dark.hex}}; + --surface: {{colors.surface_container_low.dark.hex}}; + --overlay: {{colors.surface_container.dark.hex}}; + --hl_low: {{colors.surface_container_high.dark.hex}}; + --hl_med: {{colors.surface_container_highest.dark.hex}}; + --hl_high: {{colors.surface_bright.dark.hex}}; + --outline: {{colors.outline_variant.dark.hex}}; + --muted: {{colors.outline.dark.hex}}; + --subtle: {{colors.on_surface_variant.dark.hex}}; + --text: {{colors.on_surface.dark.hex}}; + --primary: {{colors.primary.dark.hex}}; + --secondary: {{colors.secondary.dark.hex}}; + --tertiary: {{colors.tertiary.dark.hex}}; + --error: {{colors.error.dark.hex}}; + } +} +@media (prefers-color-scheme: light) { + * { + --base: {{colors.background.light.hex}}; + --surface: {{colors.surface_container_low.light.hex}}; + --overlay: {{colors.surface_container.light.hex}}; + --hl_low: {{colors.surface_container_high.light.hex}}; + --hl_med: {{colors.surface_container_highest.light.hex}}; + --hl_high: {{colors.surface_bright.light.hex}}; + --outline: {{colors.outline_variant.light.hex}}; + --muted: {{colors.outline.light.hex}}; + --subtle: {{colors.on_surface_variant.light.hex}}; + --text: {{colors.on_surface.light.hex}}; + --primary: {{colors.primary.light.hex}}; + --secondary: {{colors.secondary.light.hex}}; + --tertiary: {{colors.tertiary.light.hex}}; + --error: {{colors.error.light.hex}}; + } +} + +:root { + /* --- Window & Toolbars --- */ + --lwt-accent-color: var(--base) !important; + --lwt-accent-color-inactive: var(--base) !important; + --lwt-text-color: var(--text) !important; + --toolbar-bgcolor: var(--base) !important; + --toolbar-color: var(--text) !important; + --toolbox-bgcolor: var(--base) !important; + --toolbox-bgcolor-inactive: var(--base) !important; + + /* --- Fields (URL bar, search bar) --- */ + --toolbar-field-background-color: var(--surface) !important; + --toolbar-field-color: var(--text) !important; + --toolbar-field-focus-background-color: var(--surface) !important; + --toolbar-field-focus-color: var(--text) !important; + --toolbar-field-border-color: transparent !important; + --toolbar-field-focus-border-color: var(--primary) !important; + + /* --- Tabs --- */ + --tab-selected-bgcolor: var(--hl_med) !important; + --tab-selected-textcolor: var(--text) !important; + --tab-hover-background-color: var(--hl_low) !important; + + /* --- Toolbar buttons --- */ + --toolbarbutton-icon-fill: var(--primary) !important; + --toolbarbutton-hover-background: var(--hl_med) !important; + --toolbarbutton-active-background: var(--hl_low) !important; + + /* --- Buttons (generic) --- */ + --button-background-color: var(--hl_low) !important; + --button-background-color-hover: var(--hl_med) !important; + --button-background-color-active: var(--hl_med) !important; + --button-color: var(--text) !important; + + /* --- Panels & Menus --- */ + --arrowpanel-background: var(--overlay) !important; + --arrowpanel-color: var(--text) !important; + --arrowpanel-border-color: var(--outline) !important; + --panel-background: var(--overlay) !important; + --panel-color: var(--text) !important; + --panel-border-color: var(--outline) !important; + --panel-separator-color: var(--outline) !important; + + /* --- Sidebar --- */ + --sidebar-background-color: var(--surface) !important; + --sidebar-text-color: var(--text) !important; + --sidebar-border-color: var(--outline) !important; + + /* --- URL bar results --- */ + --urlbar-box-bgcolor: var(--hl_low) !important; + --urlbar-box-hover-bgcolor: var(--hl_med) !important; + --urlbar-box-active-bgcolor: var(--hl_low) !important; + --urlbar-box-text-color: var(--text) !important; + --urlbarView-highlight-background: var(--primary) !important; + --urlbarView-highlight-color: var(--base) !important; + --urlbarView-hover-background: var(--hl_med) !important; + + /* --- Misc --- */ + --chrome-content-separator-color: var(--outline) !important; + --focus-outline-color: var(--primary) !important; +} + +/* --- Window, Toolbars & Titlebar --- */ +#main-window, +#titlebar, +#navigator-toolbox, +#toolbar-menubar, +#TabsToolbar, +#nav-bar, +#PersonalToolbar { + background-color: var(--base) !important; + color: var(--text) !important; +} + +#navigator-toolbox { + border-bottom: 1px solid var(--outline) !important; +} + +/* Background behind web content (avoids white flash) */ +#browser, +#tabbrowser-tabbox, +.tabbrowser-tabpanels, +.browserContainer, +.browserStack { + background-color: var(--base) !important; +} + +/* --- Tabs --- */ +.tabbrowser-tab .tab-background { + background-color: var(--surface) !important; + border-radius: 8px !important; +} + +.tabbrowser-tab:hover > .tab-stack > .tab-background { + background-color: var(--hl_low) !important; +} + +.tab-background[selected], +.tab-background[multiselected] { + background-color: var(--hl_med) !important; +} + +.tab-text { + color: var(--text) !important; +} + +.tabbrowser-tab[selected] .tab-icon-image, +.tabbrowser-tab[multiselected] .tab-icon-image { + fill: var(--primary) !important; +} + +.tab-close-button { + fill: var(--muted) !important; +} + +.tab-close-button:hover { + background-color: var(--hl_med) !important; + fill: var(--text) !important; +} + +/* --- Toolbar & Navigation Icons --- */ +#back-button .toolbarbutton-icon, +#forward-button .toolbarbutton-icon, +#reload-button .toolbarbutton-icon, +#stop-button .toolbarbutton-icon, +#PanelUI-menu-button .toolbarbutton-icon, +#unified-extensions-button .toolbarbutton-icon, +#nav-bar-overflow-button .toolbarbutton-icon, +#new-tab-button .toolbarbutton-icon, +#alltabs-button .toolbarbutton-icon, +#firefox-view-button .toolbarbutton-icon { + fill: var(--primary) !important; + color: var(--primary) !important; +} + +#back-button[disabled] .toolbarbutton-icon, +#forward-button[disabled] .toolbarbutton-icon { + opacity: 0.3 !important; +} + +/* --- URL Bar --- */ +#urlbar-background { + background-color: var(--surface) !important; +} + +#urlbar[focused] #urlbar-background { + background-color: var(--surface) !important; + border-color: var(--primary) !important; + outline: 1px solid var(--primary) !important; + outline-offset: -1px; +} + +#urlbar-input { + color: var(--text) !important; +} + +#urlbar-input::selection { + background-color: var(--primary) !important; + color: var(--base) !important; +} + +#identity-icon, +#tracking-protection-icon, +#permissions-granted-icon, +#star-button .urlbar-icon, +#page-action-buttons .urlbar-icon { + fill: var(--primary) !important; + color: var(--primary) !important; +} + +.urlbarView-url { + color: var(--primary) !important; +} + +#urlbar-results { + background-color: var(--surface) !important; + color: var(--text) !important; +} + +.urlbarView-row:not([selected]) :is(.urlbarView-title, .urlbarView-action) { + color: var(--text) !important; +} + +/* --- Sidebar --- */ +#sidebar-box { + background-color: var(--surface) !important; +} + +#sidebar, +.sidebar-panel { + background-color: var(--surface) !important; + color: var(--text) !important; +} + +#sidebar-header { + background-color: var(--surface) !important; + color: var(--text) !important; + border-bottom: 1px solid var(--outline) !important; +} + +#sidebar-splitter { + background-color: var(--base) !important; +} + +#sidebar treechildren::-moz-tree-row(selected) { + background-color: var(--primary) !important; +} + +#sidebar treechildren::-moz-tree-row(hover) { + background-color: var(--hl_med) !important; +} + +#sidebar treechildren::-moz-tree-cell-text { + color: var(--text) !important; +} + +#sidebar treechildren::-moz-tree-cell-text(selected) { + color: var(--base) !important; +} + +#sidebar treechildren::-moz-tree-image { + fill: var(--text) !important; +} + +#sidebar treechildren::-moz-tree-image(selected) { + fill: var(--base) !important; +} + +/* --- Find Bar --- */ +findbar { + background-color: var(--base) !important; + color: var(--text) !important; + border-top: 1px solid var(--outline) !important; +} + +findbar .findbar-textbox { + background-color: var(--surface) !important; + color: var(--text) !important; + border: 1px solid var(--outline) !important; +} + +/* --- Status Panel --- */ +#statuspanel-label { + background-color: var(--overlay) !important; + color: var(--text) !important; + border: 1px solid var(--outline) !important; +} + +/* --- Panels & Menus --- */ +panel, +menupopup { + --panel-background: var(--overlay) !important; + --panel-color: var(--text) !important; + --panel-border-color: var(--outline) !important; +} + +.subviewbutton:hover, +.subviewbutton:focus-visible, +.unified-extensions-item:hover, +.unified-extensions-item-action-button:hover { + background-color: var(--hl_med) !important; + color: var(--text) !important; +} + +/* Context menus */ +menu[_moz-menuactive="true"]:not([disabled="true"]), +menuitem[_moz-menuactive="true"]:not([disabled="true"]) { + background-color: var(--primary) !important; + color: var(--base) !important; +} + +menu[_moz-menuactive="true"]:not([disabled="true"]) :is(.menu-text, .menu-iconic-text, .menu-accel), +menuitem[_moz-menuactive="true"]:not([disabled="true"]) :is(.menu-text, .menu-iconic-text, .menu-accel) { + color: var(--base) !important; +} + +/* --- Notification / Permission Popup --- */ +.popup-notification-body { + background-color: var(--overlay) !important; + color: var(--text) !important; +} + +/* --- Fixing Select Dropdowns on Webpages --- */ +#ContentSelectDropdown, +#ContentSelectDropdown > menupopup { + --arrowpanel-background: var(--overlay) !important; + --arrowpanel-color: var(--text) !important; + --arrowpanel-border-color: transparent !important; + --panel-border-color: transparent !important; + background-color: var(--overlay) !important; + color: var(--text) !important; + border: none !important; + border-radius: 14px !important; + overflow: hidden !important; +} + +#ContentSelectDropdown menupopup menuitem { + color: var(--text) !important; + background-color: transparent !important; +} + +#ContentSelectDropdown menupopup menuitem[_moz-menuactive="true"] { + background-color: var(--hl_low) !important; +} + +#ContentSelectDropdown menupopup menuitem[selected="true"] { + background-color: var(--hl_med) !important; +} + +#ContentSelectDropdown menupopup menuitem[_moz-menuactive="true"] *, +#ContentSelectDropdown menupopup menuitem[selected="true"] * { + color: var(--primary) !important; +} + +/* --- Autocomplete Popup (Login/Password Dropdown) --- */ +#PopupAutoComplete { + --panel-background: var(--overlay) !important; + --panel-color: var(--text) !important; + --panel-border-color: transparent !important; + --panel-padding: 0 !important; + border: none !important; + outline: none !important; + box-shadow: none !important; +} + +#PopupAutoComplete > richlistbox, +panel[type="autocomplete-richlistbox"], +.autocomplete-richlistbox { + background-color: transparent !important; + color: var(--text) !important; + border: none !important; + overflow-y: auto !important; + padding: 8px 8px !important; +} + +.autocomplete-richlistbox richlistitem, +.autocomplete-richlistitem { + background-color: var(--overlay) !important; + color: var(--text) !important; + border: none !important; +} + +.autocomplete-richlistbox richlistitem:hover, +.autocomplete-richlistitem:hover { + background-color: var(--hl_high) !important; + color: var(--text) !important; +} + +.autocomplete-richlistbox richlistitem[selected], +.autocomplete-richlistitem[selected] { + background-color: var(--primary) !important; + color: var(--base) !important; +} + +.autocomplete-richlistbox richlistitem[selected] *, +.autocomplete-richlistitem[selected] * { + color: var(--base) !important; + fill: var(--base) !important; +} + +.autocomplete-richlistbox richlistitem[selected] .ac-secondary-action, +.autocomplete-richlistitem[selected] .ac-secondary-action { + background-color: transparent !important; + fill: var(--base) !important; + color: var(--base) !important; + -moz-context-properties: fill, fill-opacity !important; +} + +/* --- Download Panel --- */ +.downloadButton { + background-color: transparent !important; + color: var(--text) !important; + border: 1px solid transparent !important; +} + +.downloadButton:hover { + background-color: var(--hl_low) !important; + color: var(--text) !important; +} + +#downloadsListBox > richlistitem[state="1"][exists].hoveringMainArea:hover, +#downloadsListBox > richlistitem.openWhenFinished.hoveringMainArea:hover, +#downloadsListBox > richlistitem[verdict]:hover, +#downloadsListBox > richlistitem.openWhenFinished:hover { + background-color: transparent !important; + color: var(--text) !important; +} + +/* --- Dialog Windows & Buttons --- */ +dialog { + background: var(--base) !important; + color: var(--text) !important; +} + +dialog button, +panel button, +window[role="dialog"] button, +window[windowtype="alert:alert"] button { + appearance: none !important; + background-color: var(--overlay) !important; + color: var(--text) !important; + border-radius: 4px !important; + border: 1px solid color-mix(in srgb, var(--text) 10%, transparent) !important; +} + +dialog button:hover:not(.downloadButton), +panel button:hover:not(.downloadButton), +window[role="dialog"] button:hover:not(.downloadButton), +window[windowtype="alert:alert"] button:hover:not(.downloadButton) { + background-color: var(--hl_low) !important; +} + +.dialog-button.primary, +dialog button.primary, +dialog button[default="true"], +panel button.primary, +panel button[default="true"], +window[role="dialog"] button[default="true"], +window[windowtype="alert:alert"] button[default="true"] { + background-color: var(--secondary) !important; + color: var(--base) !important; +} + +.dialog-button.primary:hover, +dialog button.primary:hover, +dialog button[default="true"]:hover, +panel button.primary:hover, +panel button[default="true"]:hover, +window[role="dialog"] button[default="true"]:hover, +window[windowtype="alert:alert"] button[default="true"]:hover { + background-color: color-mix(in srgb, var(--secondary) 90%, black) !important; +} + +.text-link { + color: var(--secondary) !important; +} + +.text-link:hover { + color: color-mix(in srgb, var(--secondary) 70%, transparent) !important; +} + +/* --- Checkboxes & Radios in chrome UI --- */ +*|input[type="checkbox"], +[type="checkbox"], +*|input[type="radio"], +[type="radio"] { + accent-color: var(--primary) !important; +} + +/* Custom text selection styling */ +::selection { + background-color: var(--primary) !important; + color: var(--base) !important; +} + +/* --- Bookmark Panel Folder Tree --- */ +#editBMPanel_folderTree { + background-color: var(--base) !important; + background-image: none !important; + color: var(--text) !important; + border-radius: 8px !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-row { + background-color: transparent !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-row(selected) { + background-color: var(--primary) !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-cell-text { + color: var(--text) !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-cell-text(selected) { + color: var(--base) !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-image { + fill: var(--text) !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-image(selected) { + fill: var(--base) !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-twisty { + fill: var(--text) !important; +} + +#editBMPanel_folderTree treechildren::-moz-tree-twisty(selected) { + fill: var(--base) !important; +} + +/* --- About Dialog (Help > About Firefox) --- */ +@-moz-document url("chrome://browser/content/aboutDialog.xhtml") { + #aboutDialogContainer, + #clientBox { + background-color: var(--base) !important; + } + + #bottomBox { + background-color: var(--overlay) !important; + } +} diff --git a/librewolf/librewolf-userContent.css b/librewolf/librewolf-userContent.css new file mode 100644 index 0000000..3be2f8c --- /dev/null +++ b/librewolf/librewolf-userContent.css @@ -0,0 +1,664 @@ +@media (prefers-color-scheme: dark) { + * { + --base: {{colors.background.dark.hex}}; + --surface: {{colors.surface_container_low.dark.hex}}; + --overlay: {{colors.surface_container.dark.hex}}; + --hl_low: {{colors.surface_container_high.dark.hex}}; + --hl_med: {{colors.surface_container_highest.dark.hex}}; + --hl_high: {{colors.surface_bright.dark.hex}}; + --outline: {{colors.outline_variant.dark.hex}}; + --muted: {{colors.outline.dark.hex}}; + --subtle: {{colors.on_surface_variant.dark.hex}}; + --text: {{colors.on_surface.dark.hex}}; + --primary: {{colors.primary.dark.hex}}; + --secondary: {{colors.secondary.dark.hex}}; + --tertiary: {{colors.tertiary.dark.hex}}; + --error: {{colors.error.dark.hex}}; + } +} +@media (prefers-color-scheme: light) { + * { + --base: {{colors.background.light.hex}}; + --surface: {{colors.surface_container_low.light.hex}}; + --overlay: {{colors.surface_container.light.hex}}; + --hl_low: {{colors.surface_container_high.light.hex}}; + --hl_med: {{colors.surface_container_highest.light.hex}}; + --hl_high: {{colors.surface_bright.light.hex}}; + --outline: {{colors.outline_variant.light.hex}}; + --muted: {{colors.outline.light.hex}}; + --subtle: {{colors.on_surface_variant.light.hex}}; + --text: {{colors.on_surface.light.hex}}; + --primary: {{colors.primary.light.hex}}; + --secondary: {{colors.secondary.light.hex}}; + --tertiary: {{colors.tertiary.light.hex}}; + --error: {{colors.error.light.hex}}; + } +} + +/* Common variables affecting all about: pages */ +@-moz-document url-prefix("about:") { + :root { + --in-content-page-color: var(--secondary) !important; + --color-accent-primary: var(--primary) !important; + background-color: var(--surface) !important; + --in-content-page-background: var(--overlay) !important; + } + + /* Sync with theme */ + .main-heading, + .main-search, + #content, + .sticky-container, + #search-container { + background-color: var(--base) !important; + } + + moz-toggle { + --toggle-background-color-pressed-hover: var(--primary) !important; + --toggle-background-color-pressed-active: var(--primary) !important; + --toggle-border-color: transparent !important; + --toggle-dot-background-color: var(--text) !important; + --toggle-dot-background-color-hover: var(--text) !important; + --toggle-dot-background-color-active: var(--text) !important; + --toggle-dot-background-color-on-pressed: var(--primary) !important; + --toggle-dot-margin: 3px !important; + } + + /* Input fields styling */ + moz-input-search, + moz-input-text, + input[type="text"], + input[type="search"], + input:not([type="checkbox"]):not([type="radio"]) { + background-color: var(--overlay) !important; + color: var(--text) !important; + border-color: var(--outline) !important; + border-radius: 8px !important; + --input-text-background-color: var(--text) !important; + --input-text-color: var(--text) !important; + --input-text-border-color: var(--outline) !important; + --input-text-border-radius: 8px !important; + } + + /* Message Bar styling */ + moz-message-bar { + --message-bar-background-color: var(--overlay) !important; + --message-bar-border-color: var(--outline) !important; + --message-bar-icon-color: var(--primary) !important; + background-color: var(--overlay) !important; + } + + select#input { + background: var(--overlay) !important; + color: var(--text) !important; + } + + .toggle-button { + background-color: var(--hl_med) !important; + border-color: transparent !important; + } + + .toggle-button::before { + background-color: var(--text) !important; + margin: 3px !important; + } + + .toggle-button[aria-pressed="true"], + .toggle-button[checked], + .toggle-button:checked { + background-color: var(--primary) !important; + border-color: transparent !important; + } + + .toggle-button[aria-pressed="true"]::before, + .toggle-button[checked]::before, + .toggle-button:checked::before { + background-color: var(--base) !important; + } +} + +/* Variables and styles specific to about:newtab and about:home */ +@-moz-document url("about:newtab"), url("about:home") { + :root { + --newtab-background-color: var(--overlay) !important; + --newtab-background-color-secondary: var(--surface) !important; + --newtab-element-hover-color: var(--hl_low) !important; + --newtab-text-primary-color: var(--text) !important; + --newtab-wordmark-color: var(--text) !important; + --newtab-primary-action-background: var(--primary) !important; + } + + .icon, + .card-outer:is(:hover, :focus, .active):not(.placeholder) .card-title { + color: var(--primary) !important; + } +} + +/* Variables and styles specific to about:preferences */ +@-moz-document url-prefix("about:preferences") { + :root { + --in-content-text-color: var(--text) !important; + --link-color: var(--secondary) !important; + --in-content-page-background: var(--base) !important; + --in-content-dialog-background: var(--base) !important; + } + + div.container { + background: var(--overlay) !important; + color: var(--text) !important; + } + + div.picker-item.image-item { + background: var(--overlay) !important; + color: var(--text) !important; + } + + button, + groupbox menulist { + background: var(--overlay) !important; + color: var(--text) !important; + } + + .dialogBox[dialogtype="accept"], + button[dialogtype="accept"], + .dialog-button-box button[dlgtype="accept"], + #searchSection dialog button, + #privacySection dialog button, + #syncSection dialog button, + moz-button[accent] { + background-color: var(--secondary) !important; + color: var(--base) !important; + } + moz-button[accent]:hover { + background-color: color-mix(in srgb, var(--secondary) 70%, black) !important; + } + + moz-card, + .main-content { + background-color: var(--base) !important; + } + + li.status-ok::before { + -moz-context-properties: fill !important; + fill: var(--primary) !important; + color: var(--primary) !important; + } + + .text-content.has-icon > img.icon { + fill: var(--primary) !important; + stroke: var(--primary) !important; + } + + vbox.navigation, + vbox#updateSettingsContainer.info-box-container { + background: var(--overlay) !important; + color: var(--text) !important; + } + + /* Container tabs selected row */ + richlistitem[selected="true"] { + background-color: var(--primary) !important; + color: var(--base) !important; + } + + /* Only set child text color for non-button elements */ + richlistitem[selected="true"] *:not(button):not(button *) { + color: var(--base) !important; + } + + /* Hover states for Settings and Remove buttons */ + richlistitem button:hover { + background-color: var(--hl_low) !important; + } + + /* Sub-dialog popup frame (this wrapper is in the about:preferences document) */ + dialog, + dialog#containers-dialog, + .dialogBox, + .dialog-content-box { + background-color: var(--base) !important; + border: 1px solid var(--outline) !important; + border-radius: 8px !important; + } + + /* Sub-dialog Close (X) button hover effect */ + button.dialogClose { + border-radius: 4px !important; + transition: background-color 0.2s ease !important; + } + button.dialogClose:hover { + background-color: var(--hl_low) !important; + } + + .back-button, + .subcategory-back-button, + .page-back-button, + .navigation-back-button, + moz-button[ghost="true"] button, + moz-button[type="ghost"] button, + button:has(img[src*="arrow-left.svg"]) { + appearance: none !important; + background-color: transparent !important; + border: none !important; + box-shadow: none !important; + list-style-image: url("chrome://global/skin/icons/arrow-left.svg") !important; + color: var(--text) !important; + -moz-context-properties: fill, fill-opacity !important; + fill: var(--text) !important; + width: 32px !important; + height: 32px !important; + padding: 0 !important; + display: flex !important; + justify-content: center !important; + align-items: center !important; + } + + moz-button.back-button::part(button), + moz-button.subcategory-back-button::part(button), + moz-button.page-back-button::part(button), + moz-button.navigation-back-button::part(button), + moz-button[ghost="true"]::part(button), + moz-button[type="ghost"]::part(button) { + background-color: transparent !important; + border: none !important; + box-shadow: none !important; + } + + .back-button .button-icon, + .back-button .toolbarbutton-icon, + .subcategory-back-button .button-icon, + .subcategory-back-button .toolbarbutton-icon, + .page-back-button .button-icon, + .page-back-button .toolbarbutton-icon, + .navigation-back-button .button-icon, + .navigation-back-button .toolbarbutton-icon { + -moz-context-properties: fill, fill-opacity !important; + fill: var(--text) !important; + color: var(--text) !important; + margin: 0 !important; + } + + .back-button:hover, + .subcategory-back-button:hover, + .page-back-button:hover, + .navigation-back-button:hover, + moz-button.back-button:hover button, + moz-button.subcategory-back-button:hover button, + moz-button.page-back-button:hover button, + moz-button.navigation-back-button:hover button, + moz-button[ghost="true"]:hover button, + moz-button[type="ghost"]:hover button, + moz-button.back-button:hover::part(button), + moz-button.subcategory-back-button:hover::part(button), + moz-button.page-back-button:hover::part(button), + moz-button.navigation-back-button:hover::part(button), + moz-button[ghost="true"]:hover::part(button), + moz-button[type="ghost"]:hover::part(button), + button:has(img[src*="arrow-left.svg"]):hover { + background-color: var(--hl_med) !important; + border-radius: 4px !important; + } +} + +/* --- All Preferences Sub-Dialogs (Containers, Fonts, Connection, Language, etc.) --- */ +@-moz-document url-prefix("chrome://browser/content/preferences/dialogs/"), + url("chrome://browser/content/preferences/connection.xhtml"), + url("chrome://browser/content/preferences/connections.xhtml"), + url("chrome://browser/content/preferences/fonts.xhtml"), + url("chrome://browser/content/preferences/containers.xhtml") { + window, + dialog, + #FontsDialog { + background-color: var(--base) !important; + color: var(--text) !important; + } + + /* Inner content area with Name/Color/Icon fields */ + #containers-content, + .contentPane, + vbox { + background-color: var(--overlay) !important; + color: var(--text) !important; + padding: 14px !important; + } + + /* Labels and text elements */ + label, + description, + groupbox, + checkbox { + color: var(--text) !important; + } + + separator { + border-color: var(--outline) !important; + } + + /* Text input for container name / sizes / urls */ + *|input:not([type="checkbox"]):not([type="radio"]), + *|textarea { + background-color: var(--surface) !important; + color: var(--text) !important; + border-color: var(--outline) !important; + border-radius: 6px !important; + transition: border-color 0.15s ease !important; + } + + *|input:not([type="checkbox"]):not([type="radio"]):focus, + *|textarea:focus { + border-color: var(--primary) !important; + outline: 1px solid var(--primary) !important; + outline-offset: -1px !important; + } + + /* Dropdowns */ + menulist, + *|select { + background-color: var(--surface) !important; + color: var(--text) !important; + border: 1px solid var(--outline) !important; + border-radius: 6px !important; + } + + /* Tree Columns (List headers) */ + treecol { + appearance: none !important; + background-color: var(--surface) !important; + color: var(--text) !important; + border: none !important; + box-shadow: none !important; + border-radius: 0 !important; + } + + /* Language Selection Box / Lists */ + richlistbox { + background-color: var(--surface) !important; + color: var(--text) !important; + border-color: var(--outline) !important; + } + + richlistitem { + background-color: transparent !important; + color: var(--text) !important; + } + + /* Override AccentColor for the whole document */ + :root { + --color-accent-primary: var(--primary); + --color-accent-primary-hover: var(--primary); + --color-accent-primary-active: var(--primary); + --checkbox-checked-bgcolor: var(--primary); + --checkbox-checked-border-color: var(--primary); + --radio-checked-bgcolor: var(--primary); + --radio-checked-border-color: var(--primary); + } + + /* accent-color on HTML host elements */ + checkbox, + radio, + input[type="checkbox"], + input[type="radio"] { + accent-color: var(--primary) !important; + } + + /* XUL Radio & Checkbox Native Appearance Stripping & Manual Styling */ + radio > .radio-check, + checkbox > .checkbox-check { + -moz-appearance: none !important; + appearance: none !important; + display: inline-block !important; + box-sizing: border-box !important; + width: 16px !important; + height: 16px !important; + border: 1px solid var(--outline) !important; + background-color: transparent !important; + flex-shrink: 0 !important; + } + + radio > .radio-check { + border-radius: 50% !important; + } + + checkbox > .checkbox-check { + border-radius: 3px !important; + } + + /* Radio: selected state — primary dot on base background */ + radio[selected="true"] > .radio-check, + radio[selected] > .radio-check, + radio:checked > .radio-check { + border-color: var(--primary) !important; + background-color: var(--base) !important; + background-image: radial-gradient(circle, var(--primary) 0%, var(--primary) 40%, transparent 45%) !important; + box-shadow: none !important; + } + + /* Checkbox: checked state — primary fill with native SVG checkmark */ + checkbox[checked="true"] > .checkbox-check, + checkbox[checked] > .checkbox-check, + checkbox:checked > .checkbox-check { + border-color: var(--primary) !important; + background-color: var(--primary) !important; + background-image: url("chrome://global/skin/icons/check.svg") !important; + background-repeat: no-repeat !important; + background-position: center !important; + background-size: 12px !important; + -moz-context-properties: fill !important; + fill: var(--base) !important; + } + + /* Done / Cancel buttons */ + button, + dialog::part(button) { + background-color: var(--overlay) !important; + color: var(--text) !important; + border-radius: 6px !important; + margin-top: 16px !important; + } + + button:hover { + background-color: var(--hl_med) !important; + } +} + +/* --- Primary Password Dialog (chrome://mozapps) --- */ +@-moz-document url("chrome://mozapps/content/preferences/changemp.xhtml"), + url-prefix("chrome://mozapps/content/preferences/") { + /* Outer frame */ + window, + dialog, + #changemp { + background-color: var(--base) !important; + color: var(--text) !important; + } + + /* Content boxes — uniform overlay fill with padding */ + vbox, + groupbox { + background-color: var(--overlay) !important; + color: var(--text) !important; + padding: 10px !important; + } + + /* Button row — same overlay fill, no extra padding */ + hbox { + background-color: var(--overlay) !important; + padding: 0 !important; + } + + label, + description { + color: var(--text) !important; + background-color: transparent !important; + } + + /* Password inputs */ + *|input:not([type="checkbox"]):not([type="radio"]) { + background-color: var(--surface) !important; + color: var(--text) !important; + border: 1px solid var(--outline) !important; + border-radius: 6px !important; + transition: border-color 0.15s ease !important; + } + + *|input:not([type="checkbox"]):not([type="radio"]):focus { + border-color: var(--primary) !important; + outline: 1px solid var(--primary) !important; + outline-offset: -1px !important; + } + + /* Password quality meter */ + meter { + accent-color: var(--primary) !important; + } + + /* Buttons — transparent so they match their surrounding background */ + button, + dialog::part(button) { + background-color: transparent !important; + color: var(--text) !important; + border: none !important; + border-radius: 6px !important; + } + + button:hover { + box-shadow: 0 0 0 1px var(--hl_low) !important; + background-color: transparent !important; + } +} + +/* Variables and styles specific to about:addons */ +@-moz-document url-prefix("about:addons") { + :root { + --background-color-box: var(--overlay) !important; + } + + div#sidebar { + background: var(--surface) !important; + color: var(--text) !important; + } +} + +/* Variables and styles specific to about:logins */ +@-moz-document url-prefix("about:logins") { + :root { + --background-color-box: var(--overlay) !important; + --in-content-box-background: var(--overlay) !important; + --background-color-canvas: var(--error) !important; + } + + header { + background-color: var(--base) !important; + } + + input[type="text"], + input[type="password"], + input[type="search"], + input:not([type="checkbox"]):not([type="radio"]) { + padding: 8px 12px !important; + } + + /* Restore left padding for the search input to avoid icon overlap */ + input.filter:not([type="checkbox"]):not([type="radio"]) { + padding-left: 32px !important; + } +} + +/* Variables and styles specific to about:protections */ +@-moz-document url-prefix("about:protections") { + :root { + --in-content-primary-button-text-color-hover: var(--text) !important; + --in-content-primary-button-background: var(--overlay) !important; + --in-content-primary-button-text-color: var(--text) !important; + } + + .body { + background: var(--overlay) !important; + } + + .card { + background-color: var(--base) !important; + } + + div#social.tab-content { + background-color: var(--overlay) !important; + color: var(--text) !important; + } +} + +/* Variables and styles specific to about:config */ +@-moz-document url-prefix("about:config") { + div#toolbar { + background: var(--surface) !important; + } + + button#warningButton { + background-color: var(--secondary) !important; + color: var(--base) !important; + } + + button#warningButton:hover { + background-color: color-mix(in srgb, var(--secondary) 80%, transparent) !important; + color: var(--base) !important; + } +} + +@-moz-document url-prefix("about:cache"), url-prefix("about:profiles") { + td { + background-color: var(--overlay) !important; + color: var(--text) !important; + } +} + +@-moz-document url-prefix("about:profiles") { + div.action-box { + background-color: var(--overlay) !important; + color: var(--text) !important; + } +} + +@-moz-document url-prefix("about:home") { + div.tile { + background-color: var(--base) !important; + } +} + +/* Custom text selection styling */ +::selection { + background-color: var(--primary) !important; + color: var(--base) !important; +} + +/* Style checkboxes and radio buttons globally in content */ +*|input[type="checkbox"], +[type="checkbox"], +*|input[type="radio"], +[type="radio"] { + accent-color: var(--primary) !important; +} + +/* Style external links globally in content (like New Tab settings) */ +.external-link { + color: var(--secondary) !important; +} +.external-link:hover { + color: color-mix(in srgb, var(--secondary) 70%, transparent) !important; +} + +/* Style new tab customize panel and its wrapper with a darker base background */ +.customize-menu-animate-wrapper { + background-color: var(--base) !important; +} +.customize-menu, +.close-button-wrapper { + background-color: transparent !important; +} + +/* Style personalize (Customize) button on New Tab page */ +.personalize-button { + background-color: color-mix(in srgb, var(--subtle) 40%, transparent) !important; + color: var(--text) !important; + border: 1px solid color-mix(in srgb, var(--outline) 20%, transparent) !important; + backdrop-filter: blur(10px); + transition: background-color 0.2s ease, transform 0.2s ease !important; +} diff --git a/librewolf/screenshot.png b/librewolf/screenshot.png new file mode 100644 index 0000000..0caef55 Binary files /dev/null and b/librewolf/screenshot.png differ diff --git a/librewolf/template.toml b/librewolf/template.toml new file mode 100644 index 0000000..bf9eaad --- /dev/null +++ b/librewolf/template.toml @@ -0,0 +1,13 @@ +[catalog.librewolf] +name = "LibreWolf" +category = "browser" + +[templates.librewolf_chrome] +input_path = "librewolf-userChrome.css" +output_path = "$XDG_CACHE_HOME/noctalia/librewolf/librewolf-userChrome.css" +post_hook = "bash '{{ config_dir }}/apply.sh'" + +[templates.librewolf_content] +input_path = "librewolf-userContent.css" +output_path = "$XDG_CACHE_HOME/noctalia/librewolf/librewolf-userContent.css" +post_hook = "bash '{{ config_dir }}/apply.sh'"