commit 870c6176c678f8c2a60500322b66eb93e6399def Author: Louis Dalibard Date: Tue Dec 24 16:04:27 2024 +0100 first commit diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..623e044 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +--- +version: 2 + +updates: + - package-ecosystem: github-actions + + commit-message: + prefix: ci + + directory: / + + schedule: + interval: daily diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000..d7fa314 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,45 @@ +# Derived from https://github.com/NixOS/nixpkgs/blob/2ab6f6d61630889491f86396b27a76ffb6fbc7bb/.github/workflows/backport.yml + +name: Backport + +on: + pull_request_target: + types: [closed, labeled] + +permissions: {} + +jobs: + backport: + name: Backport + runs-on: ubuntu-latest + + if: > + ( + github.repository_owner == 'danth' && + github.event.pull_request.merged == true && + (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) + ) + + steps: + # Use a GitHub App rather than the default token so that GitHub Actions + # workflows may run on the created pull request. + - name: Create GitHub access token + uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.BACKPORT_APP_ID }} + private-key: ${{ secrets.BACKPORT_PRIVATE_KEY }} + + - name: Checkout original pull request + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + token: ${{ steps.app-token.outputs.token }} + + - name: Create backport pull request + uses: korthout/backport-action@v3 + with: + github_token: ${{ steps.app-token.outputs.token }} + pull_title: "[${target_branch}] ${pull_title}" + pull_description: |- + This is an automated backport of #${pull_number}. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f26fb3f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,61 @@ +name: Build + +on: + push: + branches: + - master + - release-** + pull_request: + +permissions: + contents: read + +jobs: + evaluate: + name: List packages + runs-on: ubuntu-latest + + steps: + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + extra-conf: | + extra-experimental-features = nix-command flakes + + - name: Set up cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: List packages + id: list-packages + run: | + nix flake show github:${{ github.repository }}/${{ github.event.pull_request.head.sha || github.sha }} --json | jq -rc 'to_entries | map(.key as $type | select($type == "checks" or $type == "packages") | .value | to_entries | map(.key as $arch | select($arch == "x86_64-linux" or $arch == "x86_64-darwin") | .value | to_entries | map({type: $type, arch: $arch, os: (if $arch == "x86_64-linux" then "ubuntu-latest" else "macos-latest" end), key: .key})) | flatten) | flatten | "packages=\(.)"' >> $GITHUB_OUTPUT + + outputs: + packages: ${{ steps.list-packages.outputs.packages }} + + build: + needs: evaluate + + strategy: + fail-fast: false + matrix: + build: ${{ fromJSON(needs.evaluate.outputs.packages) }} + + name: ${{ matrix.build.key }} on ${{ matrix.build.arch }} + + runs-on: ${{ matrix.build.os }} + + steps: + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + extra-conf: | + extra-experimental-features = nix-command flakes + + - name: Set up cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Build ${{ matrix.build.key }} + run: nix -L build github:${{ github.repository }}/${{ github.event.pull_request.head.sha || github.sha }}#${{ matrix.build.type }}.${{ matrix.build.arch }}.${{ matrix.build.key }} --no-update-lock-file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..7f49711 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,57 @@ +name: Docs + +on: + push: + branches: + - master + +jobs: + build: + name: Build + + permissions: + contents: read + + runs-on: ubuntu-latest + + steps: + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + extra-conf: | + extra-experimental-features = nix-command flakes + + - name: Set up cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Build docs + run: nix -L build github:${{ github.repository }}/${{ github.sha }}#docs + + - name: Prepare docs for upload + run: cp -r --dereference --no-preserve=mode,ownership result/ public/ + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: public/ + + deploy: + name: Deploy + + needs: build + + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + runs-on: ubuntu-latest + + steps: + - name: Deploy docs to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..1a96561 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,42 @@ +name: Lint + +on: pull_request + +permissions: + contents: read + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + extra-conf: | + extra-experimental-features = nix-command flakes + + - name: Set up cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Install tools + run: | + flake='github:nixos/nixpkgs/e913ae340076bbb73d9f4d3d065c2bca7caafb16' + nix profile install "${flake}#statix" + nix profile install "${flake}#deadnix" + nix profile install "${flake}#jq" + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run statix + run: | + statix check --format json |\ + jq --raw-output '.file as $file | .report[] | .severity as $severity | .diagnostics[] | "::warning file=\($file),line=\(.at.from.line),col=\(.at.from.column)::\(.message)"' + + - name: Run deadnix + run: | + deadnix --no-lambda-arg --output-format json |\ + jq --raw-output '.file as $file | .results[] | "::warning file=\($file),line=\(.line),col=\(.column)::\(.message)"' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..750baeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +result +result-* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..836876e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2024 Daniel Thwaites and the Stylix contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f9dff0 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# Stylix + +Stylix is a NixOS module which applies the same colour scheme, font and +wallpaper to a range of applications and desktop environments. + +## What's this? + +[base16.nix](https://github.com/SenchoPens/base16.nix#readme) allows you to +import colours from [base16](https://github.com/chriskempson/base16#readme) +into Nix code. Stylix takes this a step further: + +- Automatically colours and changes the font of apps +- Sets your wallpaper +- Exports the colour scheme to be used manually for anything we missed +- Can also generate themes based on an image + +For those not familiar with [NixOS](https://nixos.org/) and +[Home Manager](https://github.com/nix-community/home-manager#readme): + +- NixOS is a Linux distribution +- Home Manager is a program which runs anywhere +- Both use the Nix language and package manager +- Both let you install programs and change settings via code + +Stylix supports either NixOS + Home Manager, or Home Manager on its own. +Certain features are only available with NixOS. + +## Resources + +Please refer to the [Stylix book](https://danth.github.io/stylix/) +for instructions and a list of supported apps. + +For a visual guide, watch the [*Ricing Linux Has Never Been Easier | NixOS + +Stylix*](https://youtu.be/ljHkWgBaQWU) YouTube video by +[Vimjoyer](https://www.youtube.com/@vimjoyer). + +> [!NOTE] +> +> It's now necessary to include `stylix.enable = true` in your configuration +> for any other settings to take effect. This is not mentioned in the video +> linked above. + +If you have any questions, you are welcome to +join our [Matrix room](https://matrix.to/#/#stylix:danth.me), +or ask on [GitHub Discussions](https://github.com/danth/stylix/discussions). + +## Example configurations + +### GNOME 46 + +![GNOME 46](./gnome.png) + +Photos by [Clay Banks](https://unsplash.com/photos/three-bicycles-parked-in-front-of-building-hwLAI5lRhdM) +and [Derrick Cooper](https://unsplash.com/photos/brown-road-in-forest-during-daytime-L505cPnmIds). + +Try a live demo of this theme by running +`nix run github:danth/stylix#testbed-gnome-light` or +`nix run github:danth/stylix#testbed-gnome-dark`. + +### KDE Plasma 5 + +![KDE Plasma 5](./kde.png) + +Photos by [Aniket Deole](https://unsplash.com/photos/mountain-surrounded-by-trees-under-cloudy-sky-T-tOgjWZ0fQ) +and [Tom Gainor](https://unsplash.com/photos/landscape-photography-of-body-of-water-overlooking-mountain-range-ZqLeQDjY6fY). + +KDE theming is still a work in progress - so some manual steps may be needed +to apply the settings completely. diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..80aeb43 --- /dev/null +++ b/default.nix @@ -0,0 +1,6 @@ +(import (let lock = builtins.fromJSON (builtins.readFile ./flake.lock); +in fetchTarball { + url = + "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; +}) { src = ./.; }).defaultNix diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000..c16729c --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,9 @@ +[book] +title = "Stylix" +language = "en" + +[output.html] +site-url = "/stylix/" +git-repository-url = "https://github.com/danth/stylix" +edit-url-template = "https://github.com/danth/stylix/edit/master/docs/{path}" + diff --git a/docs/default.nix b/docs/default.nix new file mode 100644 index 0000000..5b524cc --- /dev/null +++ b/docs/default.nix @@ -0,0 +1,66 @@ +{ pkgs, lib, inputs, ... }: + +let + makeOptionsDoc = configuration: pkgs.nixosOptionsDoc { + inherit (configuration) options; + + # Filter out any options not beginning with `stylix` + transformOptions = option: option // { + visible = option.visible && + builtins.elemAt option.loc 0 == "stylix"; + }; + }; + + nixos = makeOptionsDoc + (lib.nixosSystem { + inherit (pkgs) system; + modules = [ + inputs.home-manager.nixosModules.home-manager + inputs.self.nixosModules.stylix + ./settings.nix + ]; + }); + + homeManager = makeOptionsDoc + (inputs.home-manager.lib.homeManagerConfiguration { + inherit pkgs; + modules = [ + inputs.self.homeManagerModules.stylix + ./settings.nix + { + home = { + homeDirectory = "/home/book"; + stateVersion = "22.11"; + username = "book"; + }; + } + ]; + }); + +in pkgs.stdenvNoCC.mkDerivation { + name = "stylix-book"; + src = ./.; + + patchPhase = '' + cp ${../README.md} src/README.md + cp ${../gnome.png} src/gnome.png + cp ${../kde.png} src/kde.png + + # mdBook doesn't support this Markdown extension yet + substituteInPlace **/*.md \ + --replace-quiet '> [!NOTE]' '> **Note**' \ + --replace-quiet '> [!TIP]' '> **Tip**' \ + --replace-quiet '> [!IMPORTANT]' '> **Important**' \ + --replace-quiet '> [!WARNING]' '> **Warning**' \ + --replace-quiet '> [!CAUTION]' '> **Caution**' + + # The "declared by" links point to a file which only exists when the docs + # are built locally. This removes the links. + sed '/*Declared by:*/,/^$/d' <${nixos.optionsCommonMark} >>src/options/nixos.md + sed '/*Declared by:*/,/^$/d' <${homeManager.optionsCommonMark} >>src/options/hm.md + ''; + + buildPhase = '' + ${pkgs.mdbook}/bin/mdbook build --dest-dir $out + ''; +} diff --git a/docs/settings.nix b/docs/settings.nix new file mode 100644 index 0000000..dc1ee73 --- /dev/null +++ b/docs/settings.nix @@ -0,0 +1,26 @@ +# Dummy values to avoid errors when generating the documentation. + +{ + stylix = { + image = "/a/b/c"; + + base16Scheme = { + base00 = "ffffff"; + base01 = "ffffff"; + base02 = "ffffff"; + base03 = "ffffff"; + base04 = "ffffff"; + base05 = "ffffff"; + base06 = "ffffff"; + base07 = "ffffff"; + base08 = "ffffff"; + base09 = "ffffff"; + base0A = "ffffff"; + base0B = "ffffff"; + base0C = "ffffff"; + base0D = "ffffff"; + base0E = "ffffff"; + base0F = "ffffff"; + }; + }; +} diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000..cf475f1 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,19 @@ +[Introduction](README.md) + +# User guide + +- [Installation](installation.md) +- [Configuration](configuration.md) +- [Tips and tricks](tricks.md) + +# Reference + +- [NixOS options](options/nixos.md) +- [Home Manager options](options/hm.md) + +# Contributing + +- [Commit Convention](commit_convention.md) +- [Adding modules](modules.md) +- [Testbeds](testbeds.md) +- [Style guide](styling.md) diff --git a/docs/src/commit_convention.md b/docs/src/commit_convention.md new file mode 100644 index 0000000..0f217e7 --- /dev/null +++ b/docs/src/commit_convention.md @@ -0,0 +1,30 @@ +# Commit Convention + +To keep things consistent, commit messages should follow a format +[similar to Nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions): + +``` +«scope»: «summary» + +«motivation for change» +``` + +Where the scope is one of: + +| Scope | Purpose | +|----------------|------------------------------------------------------------------------| +| `ci` | Changes to GitHub Actions workflows. | +| `doc` | Changes to the website, `README.md`, and so on. | +| `stylix` | Changes in the `stylix` directory, `flake.nix`, and other global code. | +| Name of target | Changes to code for a particular target. | +| `treewide` | Changes across many targets. | + +The scope is meant to indicate which area of the code was changed. Specifying +the type of change, such as `feat` or `fix`, is not necessary. Dependency +updates should use whichever scope they are related to. + +The summary should start with a lowercase letter, and should not end with +punctuation. + +Most commits to `master` will also include a pull request number in brackets +after the summary. GitHub adds this automatically when creating a squash merge. diff --git a/docs/src/configuration.md b/docs/src/configuration.md new file mode 100644 index 0000000..9f51a1c --- /dev/null +++ b/docs/src/configuration.md @@ -0,0 +1,218 @@ +# Configuration + +## Enable + +To enable the Stylix module, declare: + +```nix +{ + stylix.enable = true; +} +``` + +> [!NOTE] +> +> The global enable option was recently added, so you may come across old +> examples which don't include it. No other settings will take effect unless +> `stylix.enable` is set to `true`. + +## Wallpaper + +To start theming, you need to set a wallpaper image. + +```nix +{ + stylix.image = ./wallpaper.png; +} +``` + +The option accepts derivations as well as paths, so you can fetch an image +directly from the internet: + +```nix +{ + stylix.image = pkgs.fetchurl { + url = "https://www.pixelstalk.net/wp-content/uploads/2016/05/Epic-Anime-Awesome-Wallpapers.jpg"; + sha256 = "enQo3wqhgf0FEPHj2coOCvo7DuZv+x5rL/WIo4qPI50="; + }; +} +``` + +## Color scheme + +### Generated schemes + +If you only set a wallpaper, Stylix will use a +[genetic algorithm](https://en.wikipedia.org/wiki/Genetic_algorithm) +to create a color scheme. The quality of these schemes can vary, but more +colorful images tend to have better results. + +You can force a light or dark scheme using the polarity option: + +```nix +{ + stylix.polarity = "dark"; +} +``` + +The current scheme can be previewed in a web browser at either +[`/etc/stylix/palette.html`](file:///etc/stylix/palette.html) for NixOS, or +`~/.config/stylix/palette.html` for Home Manager. + +### Handmade schemes + +If you prefer a handmade color scheme, you can choose anything from +[the Tinted Theming repository](https://github.com/tinted-theming/schemes): + +```nix +{ + stylix.base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-dark-hard.yaml"; +} +``` + +This option also accepts other files and formats supported by +[`mkSchemeAttrs`](https://github.com/SenchoPens/base16.nix/blob/main/DOCUMENTATION.md#mkschemeattrs). + +### Overriding + +For convenience, it is possible to override parts of `stylix.base16Scheme` using +`stylix.override`. Anything that +[base16.nix](https://github.com/SenchoPens/base16.nix) accepts as override is +valid. + +When using both the Home Manager and NixOS modules, both the system overrides +and the user-provided one are used in the user configuration if +`stylix.base16Scheme` is not changed in the user config. If that is the case, +only the user override is used. + +### Extending + +When passing colors to unsupported targets or creating custom modules, it +is possible to access values from the configured color scheme through +`config.lib.stylix.colors`. +An overview of the available values is shown below. + +```nix +config.lib.stylix.colors = { + base08 = "ff0000"; + base08-hex-r = "ff"; + base08-dec-r = "0.996094"; + # ... + red = "ff0000"; + # ... + withHashtag = { + base08 = "#ff0000"; + # ... + }; +}; +``` + +This attrset is generated by `mkSchemeAttrs` from `base16.nix`. Refer to the +[documentation](https://github.com/SenchoPens/base16.nix/blob/main/DOCUMENTATION.md#mkschemeattrs) +for more info. + +For more complex configurations you may find it simpler to use +[mustache](http://mustache.github.io/) templates to generate output files. +See [base16.nix](https://github.com/SenchoPens/base16.nix) documentation for +usage examples. + +## Fonts + +The default combination of fonts is: + +```nix +{ + stylix.fonts = { + serif = { + package = pkgs.dejavu_fonts; + name = "DejaVu Serif"; + }; + + sansSerif = { + package = pkgs.dejavu_fonts; + name = "DejaVu Sans"; + }; + + monospace = { + package = pkgs.dejavu_fonts; + name = "DejaVu Sans Mono"; + }; + + emoji = { + package = pkgs.noto-fonts-emoji; + name = "Noto Color Emoji"; + }; + }; +} +``` + +These can be changed as you like. + +To make things look more uniform, you could replace the serif font with +the sans-serif font: + +```nix +{ + stylix.fonts.serif = config.stylix.fonts.sansSerif; +} +``` + +Or even choose monospace for everything: + +```nix +{ + stylix.fonts = { + serif = config.stylix.fonts.monospace; + sansSerif = config.stylix.fonts.monospace; + emoji = config.stylix.fonts.monospace; + }; +} +``` + +## Home Manager inheritance + +By default, if Home Manager is used as part of NixOS, then Stylix will be +automatically installed for all users, and the NixOS theme will become their +default settings. + +This is convenient for single-user systems, since you can configure everything +once at the system level and it will automatically carry over. For multi-user +systems, you can override the settings within Home Manager to select a different +theme for each user. + +You may prefer to disable inheritance entirely, and set up the Home Manager +version of Stylix yourself if required. Refer to the options +[`stylix.homeManagerIntegration.autoImport`](options/nixos.md#stylixhomemanagerintegrationautoimport) +and +[`stylix.homeManagerIntegration.followSystem`](options/nixos.md#stylixhomemanagerintegrationfollowsystem) +to customize this. + +> [!NOTE] +> +> There is a special case involving the +> [`stylix.base16Scheme`](options/nixos.md#stylixbase16scheme) +> option: +> +> If the wallpaper in a Home Manager configuration is changed, then Home Manager +> will stop inheriting the color scheme from NixOS. This allows Home Manager +> configurations to use the automatic palette generator without being overridden. +> +> Similarly, [`stylix.override`](options/nixos.md#stylixoverride) is not inherited +> if the color scheme is different. + +## Turning targets on and off + +In Stylix terms, a target is anything which can have colors, fonts or a +wallpaper applied to it. Each module in this repository should correspond to a +target of the same name. + +Each target has an option like `stylix.targets.«target».enable` to turn its +styling on or off. Normally, it's turned on automatically when the target is +installed. You can set `stylix.autoEnable = false` to opt out of this +behaviour, in which case you'll need to manually enable each target you want to +be styled. + +Targets are different between Home Manager and NixOS, and sometimes available +in both cases. If both are available, it is always correct to enable both. +The reference pages have a list of targets for [NixOS](options/nixos.md) and +[Home Manager](options/hm.md) respectively. diff --git a/docs/src/installation.md b/docs/src/installation.md new file mode 100644 index 0000000..70fdb18 --- /dev/null +++ b/docs/src/installation.md @@ -0,0 +1,132 @@ +# Installation + +## NixOS + +You can install Stylix into your NixOS configuration using [Flakes][nix-flakes]. +This will provide theming for system level programs such as bootloaders, splash +screens, and display managers. + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + stylix.url = "github:danth/stylix"; + }; + + outputs = { nixpkgs, stylix, ... }: { + nixosConfigurations."«hostname»" = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ stylix.nixosModules.stylix ./configuration.nix ]; + }; + }; +} +``` +Minimal `flake.nix` for a NixOS configuration. + +Many applications cannot be configured system wide, so Stylix will also need +[Home Manager][nix-hm] to be able to change their settings within your home +directory. + +[Installing Home Manager as a NixOS module](https://nix-community.github.io/home-manager/index.xhtml#sec-install-nixos-module) +is highly recommended if you don't use it already. This will combine it with +your existing configuration, so you don't need to run any extra commands when +you rebuild, and the theme you set in NixOS will automatically be used for Home +Manager too. + +When Stylix is installed to a NixOS configuration, it will automatically set up +its Home Manager modules if it detects that Home Manager is available. You can +theoretically use it without installing Home Manager, however most features +will be unavailable. + +## nix-darwin + +You can install Stylix into your nix-darwin configuration in a similar fashion +to NixOS via [Flakes][nix-flakes]. + +```nix +{ + inputs = { + darwin = { + url = "github:LnL7/nix-darwin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + stylix.url = "github:danth/stylix"; + }; + + outputs = { darwin, nixpkgs, stylix, ... }: { + darwinConfigurations."«hostname»" = darwin.lib.darwinSystem { + system = "aarch64-darwin"; + modules = [ stylix.darwinModules.stylix ./configuration.nix ]; + }; + }; +} +``` +Minimal `flake.nix` for a nix-darwin configuration. + +While this won't have an effect on the looks of MacOS, since we don't have the +controls to theme it like we do NixOS, it will automatically set up the [Home +Manager][nix-hm] modules for you. + +## Home Manager + +If you would prefer to use the standalone version of Home Manager, you can +install Stylix directly into your Home Manager configuration instead. This +could be useful if you are on another operating system, or a machine which +is managed by someone else. + + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + home-manager.url = "github:nix-community/home-manager"; + stylix.url = "github:danth/stylix"; + }; + + outputs = { nixpkgs, home-manager, stylix, ... }: { + homeConfigurations."«username»" = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.x86_64-linux; + modules = [ stylix.homeManagerModules.stylix ./home.nix ]; + }; + }; +} +``` +Minimal `flake.nix` for a Home Manager configuration. + +If you choose to use both NixOS and Home Manager but configure them separately, +you will need to copy the settings described below into both of your +configurations, as keeping them separate means that they cannot follow each +other automatically. + +## Without flakes + +If you haven't enabled flakes yet or don't want to use this feature, `default.nix` +re-exports all the flake outputs, without requiring flakes to be enabled. This means +that once you have a copy of this repo, using either a local checkout, +[niv](https://github.com/nmattia/niv), or any other method, you can import it to +get the NixOS module as the `nixosModules.stylix` attribute and the Home Manager +module as the `homeManagerModules.stylix` attribute. + +```nix +let + stylix = pkgs.fetchFromGitHub { + owner = "danth"; + repo = "stylix"; + rev = "..."; + sha256 = "..."; + }; +in { + imports = [ (import stylix).homeManagerModules.stylix ]; + + stylix = { + enable = true; + image = ./wallpaper.jpg; + }; +} + +``` +Example usage of the Home Manager module without flakes. + +[nix-flakes]: https://wiki.nixos.org/wiki/Flakes +[nix-hm]: https://github.com/nix-community/home-manager diff --git a/docs/src/modules.md b/docs/src/modules.md new file mode 100644 index 0000000..6e48230 --- /dev/null +++ b/docs/src/modules.md @@ -0,0 +1,119 @@ +# Adding modules + +## Development setup + +Currently the easiest way to test Stylix is to use the new code in your +actual configuration. + +You might find it useful to change the flake reference in your configuration +from `github:danth/stylix` to `git+file:/home/user/path/to/stylix` +so that you don't need to push your changes to GitHub during testing. + +Then, remember to run `nix flake lock --update-input stylix` to refresh the +flake each time you make an edit. + +Nix only reads files which are tracked by Git, so you also need to +`git add «file»` after creating a new file. + +## Module naming + +Modules should be named like `modules/«name»/«platform».nix`. For example, +`modules/avizo/hm.nix` is a Home Manager module which themes Avizo. + +The following platforms are supported: + +- NixOS (`nixos`) +- Home Manager (`hm`) +- Nix-Darwin (`darwin`) + +Correctly named modules will be imported automatically. + +Other files needed by the module can also be stored within the +`modules/«name»` folder, using any name which is not on the list above. + +## Module template + +All modules should have an enable option created using `mkEnableTarget`. +This is similar to +[`mkEnableOption`](https://nix-community.github.io/docnix/reference/lib/options/lib-options-mkenableoption/) +from the standard library, however it integrates with +[`stylix.enable`](./options/nixos.md#stylixenable) and +[`stylix.autoEnable`](./options/nixos.md#stylixautoenable) +and generates more specific documentation. + +A general format for modules is shown below. + +```nix +{ config, lib, ... }: + +{ + options.stylix.targets.«name».enable = + config.lib.stylix.mkEnableTarget "«human readable name»" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.«name».enable) { + programs.«name».backgroundColor = config.lib.stylix.colors.base00; + }; +} +``` + +The human readable name will be inserted into the following sentence: + +> Whether to enable theming for «human readable name». + +If your module will touch options outside of `programs.«name»` or `services.«name»`, +it should include an additional condition in `mkIf` to prevent any effects +when the target is not installed. + +The boolean value after `mkEnableTarget` should be changed to `false` if +one of the following applies: + +- The module requires further manual setup to work correctly. +- There is no reliable way to detect whether the target is installed, *and* + enabling it unconditionally would cause problems. + +## Testbeds + +Adding [testbeds](./testbeds.md) for new modules is encouraged, but not +mandatory. + +## How to apply colors + +Refer to the [style guide](./styling.md) to see how colors are named, +and where to use each one. + +The colors are exported under `config.lib.stylix.colors`, which originates from +[`mkSchemeAttrs`](https://github.com/SenchoPens/base16.nix/blob/main/DOCUMENTATION.md#mkschemeattrs). + +You can use the values directly: + +```nix +{ + environment.variables.MY_APPLICATION_COLOR = config.lib.stylix.colors.base05; +} +``` + +Or you can create a [Mustache](http://mustache.github.io/) template and use +it as a function. This returns a derivation which builds the template. + +```nix +{ + environment.variables.MY_APPLICATION_CONFIG_FILE = + let configFile = config.lib.stylix.colors { + template = ./config.toml.mustache; + extension = ".toml"; + }; + in "${configFile}"; +} +``` + +Setting options through an existing NixOS or Home Manager module is preferable +to generating whole files, since users will have the option of overriding things +individually. + +Also note that reading generated files with `builtins.readFile` can be very +slow and should be avoided. + +## How to apply other things + +For everything else, like fonts and wallpapers, you can just take option values +directly from `config`. See the reference pages for a list of options. diff --git a/docs/src/options/hm.md b/docs/src/options/hm.md new file mode 100644 index 0000000..99b63fe --- /dev/null +++ b/docs/src/options/hm.md @@ -0,0 +1,24 @@ +# Home Manager options + +The following options can only be set in a Home Manager configuration. + +If you combined Home Manager with your NixOS configuration, write these +options within a Home Manager section, either for all users: + +```nix +home-manager.sharedModules = [{ + stylix.targets.xyz.enable = false; +}]; +``` + +Or for a specific user: + +```nix +home-manager.users.«name» = { + stylix.targets.xyz.enable = false; +}; +``` + +[Read more about per-user themes.](../configuration.md#multi-user-configurations) + + diff --git a/docs/src/options/nixos.md b/docs/src/options/nixos.md new file mode 100644 index 0000000..bfb9de3 --- /dev/null +++ b/docs/src/options/nixos.md @@ -0,0 +1,5 @@ +# NixOS options + +The following options can only be set in a NixOS configuration. + + diff --git a/docs/src/styling.md b/docs/src/styling.md new file mode 100644 index 0000000..5d335d1 --- /dev/null +++ b/docs/src/styling.md @@ -0,0 +1,147 @@ +# Style guide + +The [base16 style guide](https://github.com/chriskempson/base16/blob/main/styling.md) +is generally targeted towards text editors. Stylix aims to support a variety of +other applications, and as such it requires its own guide to keep colours +consistent. Towards this goal we will define several common types of +applications and how to style each of them using the available colours. + +Please keep in mind that this is a general guide; there will be several +applications that don't fit into any of the groups below. In this case it is up +to the committer to make sure said application fits in stylistically with the +rest of the themed applications. + +It is also importent to note that this is a growing theming guide and when theming an application and you find the guide to be lacking in any way in +terms of direction, you are encouraged to open an issue regarding what you would like to see added to the style guide. + +## Terms + +### Alternate + +An alternate color should be used when something needs to look separate while not +being drastically different. The smaller or less common element should use the +alternate color. + +![Appearance tab in GNOME settings](https://github.com/SomeGuyNamedMy/stylix/assets/28959268/e29f9fec-7b68-45ce-95ef-90d8e787c991) + +For example, each section in this settings menu uses the alternate background color +to separate it from the rest of the window, which is using the default background. + +### On/Off + +This is for toggles or simple status indicators which have an obvious on and off state. + +![Toggles in GNOME quick settings](https://github.com/SomeGuyNamedMy/stylix/assets/28959268/710056f6-26f7-47d4-bd2f-1384185fb46a) + +In the screenshot above the Wired and Night Light buttons are on, Power Mode is off. + +### Lists and selections + +A list of items to select between, such as tabs in a web browser. The selection is +the currently active item, or there could be multiple selected depending on the use case. + +![Sidebar of Nautilus file manager](https://github.com/SomeGuyNamedMy/stylix/assets/28959268/3b893677-75e1-4190-b3ab-b07d10930b19) + +## General colors + +- Default background: base00 +- Alternate background: base01 +- Selection background: base02 +- Default text: base05 +- Alternate text: base04 +- Warning: base0A +- Urgent: base09 +- Error: base08 + +## Window Managers + +Window Managers arrange windows and provide decorations like title bars and +borders. Examples include Sway and i3. + +This does not include applications bundled with the desktop environment such as +file managers, which would fall into the general category. Desktop helpers such as +taskbars and menus are not technically part of the window manager, although they're +often configured in the same place. + +An urgent window is one which is grabbing for attention - Windows shows this by +a flashing orange taskbar icon. + +- Unfocused window border: base03 +- Focused window border: base0D +- Unfocused window border in group: base03 +- Focused window border in group: base0D +- Urgent window border: base08 +- Window title text: base05 + +## Notifications and Popups + +Notifications and popups are any application overlay intended to be displayed +over other applications. Examples include the mako notification daemon and +avizo. + +- Window border: base0D +- Low urgency background color: base06 +- Low urgency text color: base0A +- High urgency background color: base0F +- High urgency text color: base08 +- Incomplete part of progress bar: base01 +- Complete part of progress bar: base02 + +## Desktop Helpers + +Applications that fall under this group are applications that complement the +window management facilities of whatever window manager the user is using. +Examples of this include waybar and polybar, as well as the similar programs +that are part of KDE and GNOME. + +### Light text color widgets + +Refer to general colors above. + +### Dark text color widgets + +These widgets use a different text color than usual to ensure it's still +readable when the background is more vibrant. + +- Default text color: base00 +- Alternate text color: base01 +- Item on background color: base0E +- Item off background color: base0D +- Alternate item on background color: base09 +- Alternate item off background color: base02 +- List unselected background: base0D +- List selected background: base03 + +## Images + +For creating modified versions of logos, icons, etc; where we would rather the +colors be similar to the original. + +Note that the colors provided by the scheme won't necessarily match the names given +below, although most handmade schemes do. + +- Background color: base00 +- Alternate background color: base01 +- Main color: base05 +- Alternate main color: base04 +- Red: base08 +- Orange: base09 +- Yellow: base0A +- Green: base0B +- Cyan: base0C +- Blue: base0D +- Purple: base0E +- Brown: base0F + +![Recolored systemd logo](https://github.com/SomeGuyNamedMy/stylix/assets/28959268/00ba9b23-c7eb-4cbf-9f3d-aa8de159d6dd) + +Example of a modified systemd logo. The square brackets are using the main color, +which is usually be white or black depending on the polarity of the scheme. + +## Text Editors/Viewers + +Text editors are any application that can view or edit source code. +Examples include vim, helix, and bat. + +For these please refer to the official +[base16 style guide](https://github.com/chriskempson/base16/blob/main/styling.md). diff --git a/docs/src/testbed-gnome-dark.png b/docs/src/testbed-gnome-dark.png new file mode 100644 index 0000000..dfc5c4a Binary files /dev/null and b/docs/src/testbed-gnome-dark.png differ diff --git a/docs/src/testbeds.md b/docs/src/testbeds.md new file mode 100644 index 0000000..d362ec4 --- /dev/null +++ b/docs/src/testbeds.md @@ -0,0 +1,83 @@ +# Testbeds + +Stylix provides a suite of virtual machines which can be used to test and +preview themes without installing the target to your live system. + +These can be particularly helpful for: + +- Working on targets before the login screen, since you can avoid closing + your editor to see the result. +- Developing for a different desktop environment than the one you normally use. +- Reducing the risk of breaking your system while reviewing pull requests. + +Testbeds are also built by GitHub Actions for every pull request. This is less +beneficial compared to running them yourself, since it cannot visually check +the theme, however it can catch build failures which may have been missed +otherwise. + +## Creation + +New testbeds are defined by creating a file called `testbed.nix` within the +folder for the corresponding target. This file will automatically be loaded +as a NixOS module, with options such as `stylix.image` already defined. +The module should include any options necessary to install the target and +any supporting software - for example, a window manager. + +If the target can only be used through Home Manager, you can write a +Home Manager module within the NixOS module using the following format: + +```nix +{ + home-manager.sharedModules = [{ + # Write Home Manager options here + }]; +} +``` + +Using `home-manager.sharedModules` is preferred over `home-manager.users.guest` +since it allows us to easily change the username or add additional users in +the future. + +Once the module is complete, use `git add` to track the file, then the new +packages will be [available to use](#usage). + +## Usage + +You can list the available testbeds by running this command from anywhere +within the repository: + +```console +user@host:~$ nix flake show +github:danth/stylix +└───packages + └───x86_64-linux + ├───docs: package 'stylix-book' + ├───palette-generator: package 'palette-generator' + ├───testbed-gnome-dark: package 'testbed-gnome-dark' + ├───testbed-gnome-light: package 'testbed-gnome-light' + ├───testbed-kde-dark: package 'testbed-kde-dark' + └───testbed-kde-light: package 'testbed-kde-light' +``` + +(This has been edited down to only the relevant parts.) + +To start a testbed, each of which is named in the format +`testbed-«target»-«polarity»`, run the following command: + +```console +user@host:~$ nix run .#testbed-«target»-«polarity» +``` + +Any package with a name not fitting the given format is not a testbed, +and may behave differently with this command, or not work at all. + +Once the virtual machine starts, a window should open, similar to the screenshot +below. The contents of the virtual machine will vary depending on the target you +selected earlier. + +![GDM login screen with a dark background color and showing a guest user](testbed-gnome-dark.png) + +If the testbed includes a login screen, the guest user should log in +automatically when selected. Depending on the software used, you may still be +presented with a password prompt - in which case you can leave it blank and +proceed by pressing enter. diff --git a/docs/src/tricks.md b/docs/src/tricks.md new file mode 100644 index 0000000..5fdda9f --- /dev/null +++ b/docs/src/tricks.md @@ -0,0 +1,81 @@ +# Tips and tricks + +## Adjusting the brightness and contrast of a background image + +If you want to use a background image for your desktop but find it too bright or distracting, you can use the `imagemagick` package to dim the image, or adjust its brightness and contrast to suit your preference. + +Here's an example Nix expression that takes an input image, applies a brightness/contrast adjustment to it, and saves the result as a new image file: + +```nix +{ pkgs, ... }: + +let + inputImage = ./path/to/image.jpg; + brightness = -30; + contrast = 0; + fillColor = "black" +in +{ + stylix.image = pkgs.runCommand "dimmed-background.png" { } '' + ${pkgs.imagemagick}/bin/convert "${inputImage}" -brightness-contrast ${brightness},${contrast} -fill ${fillColor} $out + ''; +} +``` + +## Dynamic wallpaper generation based on selected theme + +With imagemagick, you can also dynamically generate wallpapers based on the selected theme. +Similarly, you can use a template image and repaint it for the current theme. + +```nix +{ pkgs, ... }: + +let + theme = "${pkgs.base16-schemes}/share/themes/catppuccin-latte.yaml"; + wallpaper = pkgs.runCommand "image.png" {} '' + COLOR=$(${pkgs.yq}/bin/yq -r .base00 ${theme}) + COLOR="#"$COLOR + ${pkgs.imagemagick}/bin/magick convert -size 1920x1080 xc:$COLOR $out + ''; +in { + stylix = { + image = wallpaper; + base16Scheme = theme; + }; +} +``` + +Which is neatly implemented as a single function in `lib.stylix.pixel`: + +```nix +{ pkgs, config, ... }: + +{ + stylix = { + image = config.lib.stylix.pixel "base0A"; + base16Scheme = "${pkgs.base16-schemes}/share/themes/catppuccin-latte.yaml"; + }; +} +``` + +## Completely disabling some stylix targets + +Nixpkgs module system sometimes works in non-intuitive ways, e.g. parts +of the configuration guarded by `lib.mkIf` are still being descended +into. This means that every **loaded** (and not enabled) module must +be compatible with others - in the sense that **every** option that is +mentioned in the disabled parts of the configuration still needs to be +defined somewhere. + +Sometimes that can be a problem, when your particular configuration +diverges enough from what stylix expects. In that case you can try +stubbing all the missing options in your configuration. + +Or in a much clearer fashion you can just disable offending stylix targets +by adding the following `disableModules` line next to importing stylix +itself: + +```nix +imports = [ flake.inputs.stylix.nixosModules.stylix ]; +disabledModules = [ "${flake.inputs.stylix}/modules//nixos.nix" ]; +``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7541207 --- /dev/null +++ b/flake.lock @@ -0,0 +1,259 @@ +{ + "nodes": { + "base16": { + "inputs": { + "fromYaml": "fromYaml" + }, + "locked": { + "lastModified": 1732200724, + "narHash": "sha256-+R1BH5wHhfnycySb7Sy5KbYEaTJZWm1h+LW1OtyhiTs=", + "owner": "SenchoPens", + "repo": "base16.nix", + "rev": "153d52373b0fb2d343592871009a286ec8837aec", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "base16.nix", + "type": "github" + } + }, + "base16-fish": { + "flake": false, + "locked": { + "lastModified": 1622559957, + "narHash": "sha256-PebymhVYbL8trDVVXxCvZgc0S5VxI7I1Hv4RMSquTpA=", + "owner": "tomyun", + "repo": "base16-fish", + "rev": "2f6dd973a9075dabccd26f1cded09508180bf5fe", + "type": "github" + }, + "original": { + "owner": "tomyun", + "repo": "base16-fish", + "type": "github" + } + }, + "base16-helix": { + "flake": false, + "locked": { + "lastModified": 1725860795, + "narHash": "sha256-Z2o8VBPW3I+KKTSfe25kskz0EUj7MpUh8u355Z1nVsU=", + "owner": "tinted-theming", + "repo": "base16-helix", + "rev": "7f795bf75d38e0eea9fed287264067ca187b88a9", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-helix", + "type": "github" + } + }, + "base16-vim": { + "flake": false, + "locked": { + "lastModified": 1731949548, + "narHash": "sha256-XIDexXM66sSh5j/x70e054BnUsviibUShW7XhbDGhYo=", + "owner": "tinted-theming", + "repo": "base16-vim", + "rev": "61165b1632409bd55e530f3dbdd4477f011cadc6", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-vim", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "fromYaml": { + "flake": false, + "locked": { + "lastModified": 1731966426, + "narHash": "sha256-lq95WydhbUTWig/JpqiB7oViTcHFP8Lv41IGtayokA8=", + "owner": "SenchoPens", + "repo": "fromYaml", + "rev": "106af9e2f715e2d828df706c386a685698f3223b", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "fromYaml", + "type": "github" + } + }, + "gnome-shell": { + "flake": false, + "locked": { + "lastModified": 1732369855, + "narHash": "sha256-JhUWbcYPjHO3Xs3x9/Z9RuqXbcp5yhPluGjwsdE2GMg=", + "owner": "GNOME", + "repo": "gnome-shell", + "rev": "dadd58f630eeea41d645ee225a63f719390829dc", + "type": "github" + }, + "original": { + "owner": "GNOME", + "ref": "47.2", + "repo": "gnome-shell", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733085484, + "narHash": "sha256-dVmNuUajnU18oHzBQWZm1BQtANCHaqNuxTHZQ+GN0r8=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "c1fee8d4a60b89cae12b288ba9dbc608ff298163", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1732238832, + "narHash": "sha256-sQxuJm8rHY20xq6Ah+GwIUkF95tWjGRd1X8xF+Pkk38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8edf06bea5bcbee082df1b7369ff973b91618b8d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "base16": "base16", + "base16-fish": "base16-fish", + "base16-helix": "base16-helix", + "base16-vim": "base16-vim", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "gnome-shell": "gnome-shell", + "home-manager": "home-manager", + "nixpkgs": "nixpkgs", + "systems": "systems", + "tinted-foot": "tinted-foot", + "tinted-kitty": "tinted-kitty", + "tinted-tmux": "tinted-tmux" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "tinted-foot": { + "flake": false, + "locked": { + "lastModified": 1726913040, + "narHash": "sha256-+eDZPkw7efMNUf3/Pv0EmsidqdwNJ1TaOum6k7lngDQ=", + "owner": "tinted-theming", + "repo": "tinted-foot", + "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-foot", + "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", + "type": "github" + } + }, + "tinted-kitty": { + "flake": false, + "locked": { + "lastModified": 1716423189, + "narHash": "sha256-2xF3sH7UIwegn+2gKzMpFi3pk5DlIlM18+vj17Uf82U=", + "owner": "tinted-theming", + "repo": "tinted-kitty", + "rev": "eb39e141db14baef052893285df9f266df041ff8", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-kitty", + "rev": "eb39e141db14baef052893285df9f266df041ff8", + "type": "github" + } + }, + "tinted-tmux": { + "flake": false, + "locked": { + "lastModified": 1729501581, + "narHash": "sha256-1ohEFMC23elnl39kxWnjzH1l2DFWWx4DhFNNYDTYt54=", + "owner": "tinted-theming", + "repo": "tinted-tmux", + "rev": "f0e7f7974a6441033eb0a172a0342e96722b4f14", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-tmux", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e1e47fb --- /dev/null +++ b/flake.nix @@ -0,0 +1,134 @@ +{ + inputs = { + base16-fish = { + flake = false; + url = "github:tomyun/base16-fish"; + }; + + base16-helix = { + flake = false; + url = "github:tinted-theming/base16-helix"; + }; + + base16-vim = { + flake = false; + url = "github:tinted-theming/base16-vim"; + }; + + base16.url = "github:SenchoPens/base16.nix"; + + flake-compat = { + flake = false; + url = "github:edolstra/flake-compat"; + }; + + flake-utils = { + inputs.systems.follows = "systems"; + url = "github:numtide/flake-utils"; + }; + + gnome-shell = { + flake = false; + + # TODO: Unlocking the input and pointing to official repository requires + # updating the patch: + # https://github.com/danth/stylix/pull/224#discussion_r1460339607. + url = "github:GNOME/gnome-shell/47.2"; + }; + + # The 'home-manager' input is used to generate the documentation. + home-manager = { + inputs.nixpkgs.follows = "nixpkgs"; + url = "github:nix-community/home-manager"; + }; + + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + # Interface flake systems. + systems.url = "github:nix-systems/default"; + + tinted-foot = { + flake = false; + + # Lock the tinted-foot input to prevent upstream breaking changes. + # + # Considering that Stylix eventually re-implements this input's + # functionality [1], it might be easiest to lock this input to avoid + # wasted maintenance effort. + # + # [1]: https://github.com/danth/stylix/issues/571 + url = "github:tinted-theming/tinted-foot/fd1b924b6c45c3e4465e8a849e67ea82933fcbe4"; + }; + + tinted-tmux = { + flake = false; + url = "github:tinted-theming/tinted-tmux"; + }; + + tinted-kitty = { + flake = false; + + # Lock the tinted-kitty input to prevent upstream breaking changes. + # + # Considering that Stylix eventually re-implements this input's + # functionality [1], it might be easiest to lock this input to avoid + # wasted maintenance effort. + # + # [1]: https://github.com/danth/stylix/issues/534 + url = "github:tinted-theming/tinted-kitty/eb39e141db14baef052893285df9f266df041ff8"; + }; + }; + + outputs = + { nixpkgs, base16, self, ... }@inputs: + inputs.flake-utils.lib.eachDefaultSystem ( + system: let + inherit (nixpkgs) lib; + pkgs = nixpkgs.legacyPackages.${system}; + in { + packages = let + universalPackages = { + docs = import ./docs { inherit pkgs inputs lib; }; + palette-generator = pkgs.callPackage ./palette-generator { }; + }; + + # Testbeds are virtual machines based on NixOS, therefore they are + # only available for Linux systems. + testbedPackages = lib.optionalAttrs + (lib.hasSuffix "-linux" system) + (import ./stylix/testbed.nix { inherit pkgs inputs lib; }); + in + universalPackages // testbedPackages; + } + ) + // { + nixosModules.stylix = { pkgs, ... }@args: { + imports = [ + (import ./stylix/nixos inputs { + inherit (self.packages.${pkgs.system}) palette-generator; + base16 = base16.lib args; + homeManagerModule = self.homeManagerModules.stylix; + }) + ]; + }; + + homeManagerModules.stylix = { pkgs, ... }@args: { + imports = [ + (import ./stylix/hm inputs { + inherit (self.packages.${pkgs.system}) palette-generator; + base16 = base16.lib args; + }) + ]; + }; + + darwinModules.stylix = { pkgs, ... }@args: { + imports = [ + (import ./stylix/darwin inputs { + inherit (self.packages.${pkgs.system}) palette-generator; + base16 = base16.lib args; + homeManagerModule = self.homeManagerModules.stylix; + }) + ]; + }; + }; +} diff --git a/gnome.png b/gnome.png new file mode 100644 index 0000000..7702581 Binary files /dev/null and b/gnome.png differ diff --git a/kde.png b/kde.png new file mode 100644 index 0000000..9acd4b7 Binary files /dev/null and b/kde.png differ diff --git a/modules/alacritty/hm.nix b/modules/alacritty/hm.nix new file mode 100644 index 0000000..e9974f3 --- /dev/null +++ b/modules/alacritty/hm.nix @@ -0,0 +1,54 @@ +# Documentation is available at: +# - https://alacritty.org/config-alacritty.html +# - `man 5 alacritty` +{ config, lib, ... }: + +let + colors = config.lib.stylix.colors.withHashtag; +in +{ + options.stylix.targets.alacritty.enable = config.lib.stylix.mkEnableTarget "Alacritty" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.alacritty.enable) { + programs.alacritty.settings = { + font = with config.stylix.fonts; { + normal = { + family = monospace.name; + style = "Regular"; + }; + size = sizes.terminal; + }; + window.opacity = with config.stylix.opacity; terminal; + colors = with colors; { + primary = { + foreground = base05; + background = base00; + bright_foreground = base07; + }; + selection = { + text = base05; + background = base02; + }; + cursor = { + text = base00; + cursor = base05; + }; + normal = { + black = base00; + white = base05; + inherit red green yellow blue magenta cyan; + }; + bright = { + black = base03; + white = base07; + red = bright-red; + green = bright-green; + inherit yellow; + blue = bright-blue; + magenta = bright-magenta; + cyan = bright-cyan; + }; + }; + }; + }; +} diff --git a/modules/avizo/hm.nix b/modules/avizo/hm.nix new file mode 100644 index 0000000..e67a2d3 --- /dev/null +++ b/modules/avizo/hm.nix @@ -0,0 +1,26 @@ +{ config, lib, options, ... }: + +with config.lib.stylix.colors; +with config.stylix.fonts; +let + aviOpacity = toString config.stylix.opacity.popups; +in +{ + options.stylix.targets.avizo.enable = + config.lib.stylix.mkEnableTarget "Avizo" true; + + # Referenced https://github.com/stacyharper/base16-mako + config = lib.optionalAttrs (options.services ? avizo) (lib.mkIf (config.stylix.enable && config.stylix.targets.avizo.enable) { + services.avizo = { + settings = { + default = { + background = "rgba(${base01-rgb-r}, ${base01-rgb-g}, ${base01-rgb-b}, ${aviOpacity})"; + border-color = "rgba(${base0D-rgb-r}, ${base0D-rgb-g}, ${base0D-rgb-b}, ${aviOpacity})"; + bar-fg-color = "rgba(${base05-rgb-r}, ${base05-rgb-g}, ${base05-rgb-b}, ${aviOpacity})"; + bar-bg-color = "rgba(${base01-rgb-r}, ${base01-rgb-g}, ${base01-rgb-b}, ${aviOpacity})"; + image-opacity = aviOpacity; + }; + }; + }; + }); +} diff --git a/modules/bat/base16-stylix.mustache b/modules/bat/base16-stylix.mustache new file mode 100644 index 0000000..1359154 --- /dev/null +++ b/modules/bat/base16-stylix.mustache @@ -0,0 +1,538 @@ + + + + + author + Template: Chris Kempson, Scheme: Mitchell Kember + name + Base16 Stylix + colorSpaceName + sRGB + settings + + + settings + + background + #{{base00-hex}} + caret + #{{base05-hex}} + foreground + #{{base05-hex}} + invisibles + #{{base03-hex}} + lineHighlight + #{{base03-hex}} + selection + #{{base02-hex}} + gutter + #{{base01-hex}} + gutterForeground + #{{base03-hex}} + + + + name + Text + scope + variable.parameter.function + settings + + foreground + #{{base05-hex}} + + + + name + Comments + scope + comment, punctuation.definition.comment + settings + + foreground + #{{base03-hex}} + + + + name + Punctuation + scope + punctuation.definition.string, punctuation.definition.variable, punctuation.definition.string, punctuation.definition.parameters, punctuation.definition.string, punctuation.definition.array + settings + + foreground + #{{base05-hex}} + + + + name + Delimiters + scope + none + settings + + foreground + #{{base05-hex}} + + + + name + Operators + scope + keyword.operator + settings + + foreground + #{{base05-hex}} + + + + name + Keywords + scope + keyword + settings + + foreground + #{{base0E-hex}} + + + + name + Variables + scope + variable + settings + + foreground + #{{base05-hex}} + + + + name + Functions + scope + entity.name.function, meta.require, support.function.any-method + settings + + foreground + #{{base0D-hex}} + + + + name + Labels + scope + entity.name.label + settings + + foreground + #{{base0F-hex}} + + + + name + Classes + scope + support.class, entity.name.class, entity.name.type.class, entity.name + settings + + foreground + #{{base0A-hex}} + + + + name + Classes + scope + meta.class + settings + + foreground + #{{base07-hex}} + + + + name + Methods + scope + keyword.other.special-method + settings + + foreground + #{{base0D-hex}} + + + + name + Storage + scope + storage + settings + + foreground + #{{base0E-hex}} + + + + name + Support + scope + support.function + settings + + foreground + #{{base0C-hex}} + + + + name + Strings, Inherited Class + scope + string, constant.other.symbol, entity.other.inherited-class + settings + + foreground + #{{base0B-hex}} + + + + name + Integers + scope + constant.numeric + settings + + foreground + #{{base09-hex}} + + + + name + Floats + scope + none + settings + + foreground + #{{base09-hex}} + + + + name + Boolean + scope + none + settings + + foreground + #{{base09-hex}} + + + + name + Constants + scope + constant + settings + + foreground + #{{base09-hex}} + + + + name + Tags + scope + entity.name.tag + settings + + foreground + #{{base08-hex}} + + + + name + Attributes + scope + entity.other.attribute-name + settings + + foreground + #{{base09-hex}} + + + + name + Attribute IDs + scope + entity.other.attribute-name.id, punctuation.definition.entity + settings + + foreground + #{{base0D-hex}} + + + + name + Selector + scope + meta.selector + settings + + foreground + #{{base0E-hex}} + + + + name + Values + scope + none + settings + + foreground + #{{base09-hex}} + + + + name + Headings + scope + markup.heading, punctuation.definition.heading, entity.name.section + settings + + fontStyle + + foreground + #{{base0D-hex}} + + + + name + Units + scope + keyword.other.unit + settings + + foreground + #{{base09-hex}} + + + + name + Bold + scope + markup.bold, punctuation.definition.bold + settings + + fontStyle + bold + foreground + #{{base0A-hex}} + + + + name + Italic + scope + markup.italic, punctuation.definition.italic + settings + + fontStyle + italic + foreground + #{{base0E-hex}} + + + + name + Code + scope + markup.raw.inline + settings + + foreground + #{{base0B-hex}} + + + + name + Link Text + scope + string.other.link, punctuation.definition.string.end.markdown, punctuation.definition.string.begin.markdown + settings + + foreground + #{{base08-hex}} + + + + name + Link Url + scope + meta.link + settings + + foreground + #{{base09-hex}} + + + + name + Quotes + scope + markup.quote + settings + + foreground + #{{base09-hex}} + + + + name + Separator + scope + meta.separator + settings + + background + #{{base02-hex}} + foreground + #{{base05-hex}} + + + + name + Inserted + scope + markup.inserted + settings + + foreground + #{{base0B-hex}} + + + + name + Deleted + scope + markup.deleted + settings + + foreground + #{{base08-hex}} + + + + name + Changed + scope + markup.changed + settings + + foreground + #{{base0E-hex}} + + + + name + Colors + scope + constant.other.color + settings + + foreground + #{{base0C-hex}} + + + + name + Regular Expressions + scope + string.regexp + settings + + foreground + #{{base0C-hex}} + + + + name + Escape Characters + scope + constant.character.escape + settings + + foreground + #{{base0C-hex}} + + + + name + Embedded + scope + punctuation.section.embedded, variable.interpolation + settings + + foreground + #{{base0E-hex}} + + + + name + Illegal + scope + invalid.illegal + settings + + background + #{{base08-hex}} + foreground + #{{base07-hex}} + + + + name + Broken + scope + invalid.broken + settings + + background + #{{base09-hex}} + foreground + #{{base00-hex}} + + + + name + Deprecated + scope + invalid.deprecated + settings + + background + #{{base0F-hex}} + foreground + #{{base07-hex}} + + + + name + Unimplemented + scope + invalid.unimplemented + settings + + background + #{{base03-hex}} + foreground + #{{base07-hex}} + + + + uuid + uuid + + diff --git a/modules/bat/hm.nix b/modules/bat/hm.nix new file mode 100644 index 0000000..e38751e --- /dev/null +++ b/modules/bat/hm.nix @@ -0,0 +1,19 @@ +{ config, lib, ... }: + +{ + options.stylix.targets.bat.enable = + config.lib.stylix.mkEnableTarget "Bat" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.bat.enable) { + programs.bat = { + # This theme is reused for yazi. Changes to the template + # will need to be applied to modules/yazi/hm.nix + themes."base16-stylix".src = config.lib.stylix.colors { + template = ./base16-stylix.mustache; + extension = ".tmTheme"; + }; + + config.theme = "base16-stylix"; + }; + }; +} diff --git a/modules/bemenu/hm.nix b/modules/bemenu/hm.nix new file mode 100644 index 0000000..e629e4d --- /dev/null +++ b/modules/bemenu/hm.nix @@ -0,0 +1,51 @@ +{ config, lib, ... }: + +with config.lib.stylix.colors.withHashtag; +with config.stylix.fonts; +let + bemenuOpacity = lib.toHexString (((builtins.ceil (config.stylix.opacity.popups * 100)) * 255) / 100); +in { + options.stylix.targets.bemenu = { + enable = config.lib.stylix.mkEnableTarget "bemenu" true; + + fontSize = lib.mkOption { + description = '' + Font size used for bemenu. + ''; + type = with lib.types; nullOr int; + default = sizes.popups; + }; # optional argument + + alternate = lib.mkOption { + description = '' + Whether to use alternating colours. + ''; + type = lib.types.bool; + default = false; + }; + }; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.bemenu.enable) { + programs.bemenu.settings = with config.stylix.targets.bemenu; { + tb = "${base01}${bemenuOpacity}"; # Title bg + nb = "${base01}${bemenuOpacity}"; # Normal bg + fb = "${base01}${bemenuOpacity}"; # Filter bg + hb = "${base03}${bemenuOpacity}"; # Highlighted bg + sb = "${base03}${bemenuOpacity}"; # Selected bg + scb = "${base01}"; # Scrollbar bg + + hf = "${base0A}"; # Highlighted fg + sf = "${base0B}"; # Selected fg + tf = "${base05}"; # Title fg + ff = "${base05}"; # Filter fg + nf = "${base05}"; # Normal fg + scf = "${base03}"; # Scrollbar fg + + ab = "${if alternate then base00 else base01}"; # Alternate bg + af = "${if alternate then base04 else base05}"; # Alternate fg + + # Font name + fn = "${sansSerif.name} ${lib.optionalString (fontSize != null) (builtins.toString fontSize)}"; + }; + }; +} diff --git a/modules/bspwm/hm.nix b/modules/bspwm/hm.nix new file mode 100644 index 0000000..108f41f --- /dev/null +++ b/modules/bspwm/hm.nix @@ -0,0 +1,17 @@ +{ config, lib, ... }: + +let + colors = config.lib.stylix.colors.withHashtag; +in { + options.stylix.targets.bspwm.enable = + config.lib.stylix.mkEnableTarget "bspwm" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.bspwm.enable) { + xsession.windowManager.bspwm.settings = { + normal_border_color = colors.base03; + active_border_color = colors.base0C; + focused_border_color = colors.base0D; + presel_feedback_color = colors.base00; + }; + }; +} diff --git a/modules/btop/hm.nix b/modules/btop/hm.nix new file mode 100644 index 0000000..4c7a709 --- /dev/null +++ b/modules/btop/hm.nix @@ -0,0 +1,61 @@ +{ config, lib, ... }: + +let + colors = config.lib.stylix.colors.withHashtag; +in { + options.stylix.targets.btop.enable = config.lib.stylix.mkEnableTarget "btop" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.btop.enable && config.programs.btop.enable) { + + programs.btop.settings = { + color_theme = "stylix"; + theme_background = lib.mkIf (config.stylix.opacity.terminal != 1.0) false; + }; + + xdg.configFile."btop/themes/stylix.theme".text = with colors; '' + #Generated by Stylix + theme[main_bg]="${base00}" + theme[main_fg]="${base05}" + theme[title]="${base05}" + theme[hi_fg]="${base0D}" + theme[selected_bg]="${base03}" + theme[selected_fg]="${base0D}" + theme[inactive_fg]="${base04}" + theme[graph_text]="${base06}" + theme[meter_bg]="${base03}" + theme[proc_misc]="${base06}" + theme[cpu_box]="${base0E}" + theme[mem_box]="${base0B}" + theme[net_box]="${base0C}" + theme[proc_box]="${base0D}" + theme[div_line]="${base01}" + theme[temp_start]="${base0B}" + theme[temp_mid]="${base0A}" + theme[temp_end]="${base08}" + theme[cpu_start]="${base0B}" + theme[cpu_mid]="${base0A}" + theme[cpu_end]="${base08}" + theme[free_start]="${base0B}" + theme[free_mid]="${base0A}" + theme[free_end]="${base08}" + theme[cached_start]="${base0B}" + theme[cached_mid]="${base0A}" + theme[cached_end]="${base08}" + theme[available_start]="${base0B}" + theme[available_mid]="${base0A}" + theme[available_end]="${base08}" + theme[used_start]="${base0B}" + theme[used_mid]="${base0A}" + theme[used_end]="${base08}" + theme[download_start]="${base0B}" + theme[download_mid]="${base0A}" + theme[download_end]="${base08}" + theme[upload_start]="${base0B}" + theme[upload_mid]="${base0A}" + theme[upload_end]="${base08}" + theme[process_start]="${base0B}" + theme[process_mid]="${base0A}" + theme[process_end]="${base08}" + ''; + }; +} diff --git a/modules/cava/hm.nix b/modules/cava/hm.nix new file mode 100644 index 0000000..fd88b72 --- /dev/null +++ b/modules/cava/hm.nix @@ -0,0 +1,34 @@ +{ config, lib, ... }: + +let + + mkGradient = colors: lib.listToAttrs (lib.imap0 (i: c: lib.nameValuePair "gradient_color_${toString (i+1)}" "'#${c}'") colors) // { + gradient = 1; + gradient_count = builtins.length colors; + }; + + rainbowColors = with config.lib.stylix.colors; [ + base0E + base0D + base0C + base0B + base0A + base09 + base08 + ]; + +in { + options.stylix.targets.cava = { + enable = config.lib.stylix.mkEnableTarget "CAVA" true; + rainbow.enable = config.lib.stylix.mkEnableTarget "rainbow gradient theming" false; + }; + + config = let + cfg = config.stylix.targets.cava; + in + lib.mkIf (config.stylix.enable && cfg.enable) { + programs.cava.settings.color = lib.mkIf cfg.rainbow.enable ( + mkGradient rainbowColors + ); + }; +} diff --git a/modules/chromium/nixos.nix b/modules/chromium/nixos.nix new file mode 100644 index 0000000..8f193f2 --- /dev/null +++ b/modules/chromium/nixos.nix @@ -0,0 +1,14 @@ +{ config, lib, ... }: + +{ + options.stylix.targets.chromium.enable = + config.lib.stylix.mkEnableTarget "Chromium, Google Chrome and Brave" true; + + config.programs.chromium = lib.mkIf (config.stylix.enable && config.stylix.targets.chromium.enable) { + # This enables policies without installing the browser. Policies take up a + # negligible amount of space, so it's reasonable to have this always on. + enable = true; + + extraOpts.BrowserThemeColor = config.lib.stylix.colors.withHashtag.base00; + }; +} diff --git a/modules/console/nixos.nix b/modules/console/nixos.nix new file mode 100644 index 0000000..b5b2b2b --- /dev/null +++ b/modules/console/nixos.nix @@ -0,0 +1,27 @@ +{ config, lib, ... }: + +with config.lib.stylix.colors; + +{ + options.stylix.targets.console.enable = + config.lib.stylix.mkEnableTarget "the Linux kernel console" true; + + config.console.colors = lib.mkIf (config.stylix.enable && config.stylix.targets.console.enable) [ + base00-hex + red + green + yellow + blue + magenta + cyan + base05-hex + base03-hex + red + green + yellow + blue + magenta + cyan + base06-hex + ]; +} diff --git a/modules/dunst/hm.nix b/modules/dunst/hm.nix new file mode 100644 index 0000000..7c4ad0b --- /dev/null +++ b/modules/dunst/hm.nix @@ -0,0 +1,37 @@ +{ config, lib, ... }: + +with config.lib.stylix.colors.withHashtag; +with config.stylix.fonts; +let + dunstOpacity = lib.toHexString (((builtins.ceil (config.stylix.opacity.popups * 100)) * 255) / 100); +in { + options.stylix.targets.dunst.enable = + config.lib.stylix.mkEnableTarget "Dunst" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.dunst.enable) { + services.dunst.settings = { + global = { + separator_color = base02; + font = "${sansSerif.name} ${toString sizes.popups}"; + }; + + urgency_low = { + background = base01 + dunstOpacity; + foreground = base05; + frame_color = base0B; + }; + + urgency_normal = { + background = base01 + dunstOpacity; + foreground = base05; + frame_color = base0E; + }; + + urgency_critical = { + background = base01 + dunstOpacity; + foreground = base05; + frame_color = base08; + }; + }; + }; +} diff --git a/modules/emacs/hm.nix b/modules/emacs/hm.nix new file mode 100644 index 0000000..32e187b --- /dev/null +++ b/modules/emacs/hm.nix @@ -0,0 +1,77 @@ +{ pkgs, config, lib, ... }: + +with config.lib.stylix.colors.withHashtag; +with config.stylix.fonts; + +let + emacsOpacity = builtins.toString (builtins.ceil (config.stylix.opacity.applications * 100)); + emacsSize = builtins.toString (sizes.terminal * 1.0); +in +{ + options.stylix.targets.emacs.enable = + config.lib.stylix.mkEnableTarget "Emacs" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.emacs.enable) { + programs.emacs = { + extraPackages = epkgs: + [ + (epkgs.trivialBuild { + pname = "base16-stylix-theme"; + version = "0.1.0"; + src = pkgs.writeText "base16-stylix-theme.el" '' + (require 'base16-theme) + + (defvar base16-stylix-theme-colors + '(:base00 "${base00}" + :base01 "${base01}" + :base02 "${base02}" + :base03 "${base03}" + :base04 "${base04}" + :base05 "${base05}" + :base06 "${base06}" + :base07 "${base07}" + :base08 "${base08}" + :base09 "${base09}" + :base0A "${base0A}" + :base0B "${base0B}" + :base0C "${base0C}" + :base0D "${base0D}" + :base0E "${base0E}" + :base0F "${base0F}") + "All colors for Base16 stylix are defined here.") + + ;; Define the theme + (deftheme base16-stylix) + + ;; Add all the faces to the theme + (base16-theme-define 'base16-stylix base16-stylix-theme-colors) + + ;; Mark the theme as provided + (provide-theme 'base16-stylix) + + ;; Add path to theme to theme-path + (add-to-list 'custom-theme-load-path + (file-name-directory + (file-truename load-file-name))) + + (provide 'base16-stylix-theme) + ''; + packageRequires = [ epkgs.base16-theme ]; + }) + ]; + + extraConfig = '' + ;; ---- Generated by stylix ---- + (require 'base16-stylix-theme) + (setq base16-theme-256-color-source 'colors) + (load-theme 'base16-stylix t) + ;; Set font + (set-face-attribute 'default nil :font (font-spec :family "${monospace.name}" :size ${emacsSize})) + ;; ----------------------------- + ;; set opacity + (add-to-list 'default-frame-alist '(alpha-background . ${emacsOpacity})) + ;; ----------------------------- + ''; + }; + }; +} diff --git a/modules/feh/hm.nix b/modules/feh/hm.nix new file mode 100644 index 0000000..62fccb0 --- /dev/null +++ b/modules/feh/hm.nix @@ -0,0 +1,35 @@ +{ pkgs, config, lib, ... }: + +{ + options.stylix.targets.feh.enable = + config.lib.stylix.mkEnableTarget + "the desktop background using Feh" + true; + + config.xsession.initExtra = + lib.mkIf ( + config.stylix.enable + && config.stylix.targets.feh.enable + && ( + with config.xsession.windowManager; + bspwm.enable + || herbstluftwm.enable + || i3.enable + || spectrwm.enable + || xmonad.enable + ) + ) (let + inherit (config.stylix) imageScalingMode; + bg-arg = + if imageScalingMode == "fill" + then "--bg-fill" + else if imageScalingMode == "center" + then "--bg-center" + else if imageScalingMode == "tile" + then "--bg-tile" + else if imageScalingMode == "stretch" + then "--bg-scale" + # Fit + else "--bg-max"; + in "${pkgs.feh}/bin/feh --no-fehbg ${bg-arg} ${config.stylix.image}"); +} diff --git a/modules/feh/nixos.nix b/modules/feh/nixos.nix new file mode 100644 index 0000000..74f6688 --- /dev/null +++ b/modules/feh/nixos.nix @@ -0,0 +1,33 @@ +{ pkgs, config, lib, ... }: + +{ + options.stylix.targets.feh.enable = + config.lib.stylix.mkEnableTarget + "the desktop background using Feh" + true; + + config.services.xserver.displayManager.sessionCommands = + lib.mkIf ( + config.stylix.enable + && config.stylix.targets.feh.enable + && ( + with config.services.xserver.windowManager; + xmonad.enable + || i3.enable + ) + ) + (let + inherit (config.stylix) imageScalingMode; + bg-arg = + if imageScalingMode == "fill" + then "--bg-fill" + else if imageScalingMode == "center" + then "--bg-center" + else if imageScalingMode == "tile" + then "--bg-tile" + else if imageScalingMode == "stretch" + then "--bg-scale" + # Fit + else "--bg-max"; + in "${pkgs.feh}/bin/feh --no-fehbg ${bg-arg} ${config.stylix.image}"); +} diff --git a/modules/firefox/hm.nix b/modules/firefox/hm.nix new file mode 100644 index 0000000..d8d49a6 --- /dev/null +++ b/modules/firefox/hm.nix @@ -0,0 +1,32 @@ +# Consider also updating the LibreWolf module when updating this module, +# as they are very similar. + +{ config, lib, ... }: + +let + profileSettings = { + settings = { + "font.name.monospace.x-western" = config.stylix.fonts.monospace.name; + "font.name.sans-serif.x-western" = config.stylix.fonts.sansSerif.name; + "font.name.serif.x-western" = config.stylix.fonts.serif.name; + }; + }; + makeProfileSettingsPair = profileName: + lib.nameValuePair profileName profileSettings; +in { + options.stylix.targets.firefox = { + enable = + config.lib.stylix.mkEnableTarget "Firefox" true; + + profileNames = lib.mkOption { + description = "The Firefox profile names to apply styling on."; + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + }; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.firefox.enable) { + programs.firefox.profiles = lib.listToAttrs + (map makeProfileSettingsPair config.stylix.targets.firefox.profileNames); + }; +} diff --git a/modules/fish/hm.nix b/modules/fish/hm.nix new file mode 100644 index 0000000..4493aec --- /dev/null +++ b/modules/fish/hm.nix @@ -0,0 +1,10 @@ +{ config, lib, ... }: + +{ + options.stylix.targets.fish.enable = + config.lib.stylix.mkEnableTarget "Fish" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.fish.enable) { + programs.fish.interactiveShellInit = import ./prompt.nix config; + }; +} diff --git a/modules/fish/nixos.nix b/modules/fish/nixos.nix new file mode 100644 index 0000000..2fa26dd --- /dev/null +++ b/modules/fish/nixos.nix @@ -0,0 +1,10 @@ +{ config, lib, ... }: + +{ + options.stylix.targets.fish.enable = + config.lib.stylix.mkEnableTarget "Fish" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.fish.enable) { + programs.fish.promptInit = import ./prompt.nix config; + }; +} diff --git a/modules/fish/prompt.nix b/modules/fish/prompt.nix new file mode 100644 index 0000000..f379280 --- /dev/null +++ b/modules/fish/prompt.nix @@ -0,0 +1,14 @@ +config: + +let + theme = config.lib.stylix.colors { + templateRepo = config.lib.stylix.templates.base16-fish; + }; +in '' + source ${theme} + + # See https://github.com/tomyun/base16-fish/issues/7 for why this condition exists + if status --is-interactive && test -z "$TMUX" + base16-${config.lib.stylix.colors.slug} + end +'' diff --git a/modules/foot/hm.nix b/modules/foot/hm.nix new file mode 100644 index 0000000..8fa04af --- /dev/null +++ b/modules/foot/hm.nix @@ -0,0 +1,24 @@ +{ config, lib, ... }: + +let + cfg = config.stylix.targets.foot; + + theme = config.lib.stylix.colors { + templateRepo = config.lib.stylix.templates.tinted-foot; + }; + +in { + options.stylix.targets.foot.enable = + config.lib.stylix.mkEnableTarget "Foot" true; + + config.programs.foot.settings = lib.mkIf cfg.enable { + main = { + include = theme; + font = + with config.stylix.fonts; + "${monospace.name}:size=${toString sizes.terminal}"; + dpi-aware = "no"; + }; + colors.alpha = with config.stylix.opacity; terminal; + }; +} diff --git a/modules/forge/hm.nix b/modules/forge/hm.nix new file mode 100644 index 0000000..3c5cab5 --- /dev/null +++ b/modules/forge/hm.nix @@ -0,0 +1,13 @@ +{ config, lib, ... }: + +{ + options.stylix.targets.forge.enable = + config.lib.stylix.mkEnableTarget "Forge" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.forge.enable) { + xdg.configFile."forge/stylesheet/forge/stylesheet.css".source = config.lib.stylix.colors { + template = ./stylesheet.css.mustache; + extension = ".css"; + }; + }; +} diff --git a/modules/forge/stylesheet.css.mustache b/modules/forge/stylesheet.css.mustache new file mode 100644 index 0000000..25a9608 --- /dev/null +++ b/modules/forge/stylesheet.css.mustache @@ -0,0 +1,140 @@ +.tiled { + color: #{{base0D-hex}}; + opacity: 1; + border-width: 3px; +} + +.split { + color: #{{base0D-hex}}; + opacity: 1; + border-width: 3px; +} + +.stacked { + color: #{{base0D-hex}}; + opacity: 1; + border-width: 3px; +} + +.tabbed { + color: #{{base0D-hex}}; + opacity: 1; + border-width: 3px; +} + +.floated { + color: #{{base0D-hex}}; + border-width: 3px; + opacity: 1; +} + +.window-tiled-border { + border-width: 3px; + border-color: #{{base0D-hex}}; + border-style: solid; + border-radius: 14px; +} + +.window-split-border { + border-width: 3px; + border-color: #{{base0D-hex}}; + border-style: solid; + border-radius: 14px; +} + +.window-split-horizontal { + border-left-width: 0; + border-top-width: 0; + border-bottom-width: 0; +} + +.window-split-vertical { + border-left-width: 0; + border-top-width: 0; + border-right-width: 0; +} + +.window-stacked-border { + border-width: 3px; + border-color: #{{base0D-hex}}; + border-style: solid; + border-radius: 14px; +} + +.window-tabbed-border { + border-width: 3px; + border-color: #{{base0D-hex}}; + border-style: solid; + border-radius: 14px; +} + +.window-tabbed-bg { + border-radius: 8px; +} + +.window-tabbed-tab { + background-color: rgba(54, 47, 45, 1); + border-color: #{{base0D-hex}}9A; + border-width: 1px; + border-radius: 8px; + color: white; + margin: 1px; + box-shadow: 0 0 0 1px rgba(1px 0, 0, 0, 0.2); +} + +.window-tabbed-tab-active { + background-color: #{{base0D-hex}}; + color: black; + box-shadow: 0 0 0 1px rgba(1px 0, 0, 0, 0.2); +} + +.window-tabbed-tab-close { + padding: 3px; + margin: 4px; + border-radius: 16px; + width: 16px; + background-color: #{{base08-hex}}; +} + +.window-tabbed-tab-icon { + margin: 3px; +} + +.window-floated-border { + border-width: 3px; + border-color: #{{base0D-hex}}; + border-style: solid; + border-radius: 14px; +} + +.window-tilepreview-tiled { + border-width: 1px; + border-color: #{{base0D-hex}}4D; + border-style: solid; + border-radius: 14px; + background-color: #{{base0D-hex}}33; +} + +.window-tilepreview-stacked { + border-width: 1px; + border-color: #{{base0D-hex}}66; + border-style: solid; + border-radius: 14px; + background-color: #{{base0D-hex}}4D; +} + +.window-tilepreview-swap { + border-width: 1px; + border-color: #{{base0D-hex}}4D; + border-style: solid; + border-radius: 14px; + background-color: #{{base0D-hex}}4D; +} + +.window-tilepreview-tabbed { + border-width: 1px; + border-color: #{{base0D-hex}}4D; + border-style: solid; + border-radius: 14px; + background-color: #{{base0D-hex}}4D; +} diff --git a/modules/fuzzel/hm.nix b/modules/fuzzel/hm.nix new file mode 100644 index 0000000..6b4547c --- /dev/null +++ b/modules/fuzzel/hm.nix @@ -0,0 +1,32 @@ +{ config, lib, ... }: + +with config.lib.stylix.colors; + +let + opacity = lib.toHexString (builtins.ceil (config.stylix.opacity.popups * 255)); + +in { + options.stylix.targets.fuzzel.enable = + config.lib.stylix.mkEnableTarget "Fuzzel" true; + + config.programs.fuzzel.settings = + lib.mkIf (config.stylix.enable && config.stylix.targets.fuzzel.enable) { + colors = { + background = "${base00-hex}${opacity}"; + text = "${base05-hex}ff"; + placeholder = "${base03-hex}ff"; + prompt = "${base05-hex}ff"; + input = "${base05-hex}ff"; + match = "${base0A-hex}ff"; + selection = "${base03-hex}ff"; + selection-text = "${base05-hex}ff"; + selection-match = "${base0A-hex}ff"; + counter = "${base06-hex}ff"; + border = "${base0D-hex}ff"; + }; + + main = { + font = "${config.stylix.fonts.sansSerif.name}:size=${toString config.stylix.fonts.sizes.popups}"; + }; + }; +} diff --git a/modules/fzf/hm.nix b/modules/fzf/hm.nix new file mode 100644 index 0000000..28ca4c5 --- /dev/null +++ b/modules/fzf/hm.nix @@ -0,0 +1,23 @@ +{ config, lib, ... }: +{ + options.stylix.targets.fzf = { + enable = config.lib.stylix.mkEnableTarget "Fzf" true; + }; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.fzf.enable) { + programs.fzf.colors = with config.lib.stylix.colors.withHashtag; { + "bg" = base00; + "bg+" = base01; + "fg" = base04; + "fg+" = base06; + "header" = base0D; + "hl" = base0D; + "hl+" = base0D; + "info" = base0A; + "marker" = base0C; + "pointer" = base0C; + "prompt" = base0A; + "spinner" = base0C; + }; + }; +} diff --git a/modules/gedit/hm.nix b/modules/gedit/hm.nix new file mode 100644 index 0000000..4f974d5 --- /dev/null +++ b/modules/gedit/hm.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: + +let + style = config.lib.stylix.colors { + template = ./template.mustache; + extension = "xml"; + }; + +in { + options.stylix.targets.gedit.enable = + config.lib.stylix.mkEnableTarget "GEdit" true; + + config = lib.mkIf (config.stylix.enable && config.stylix.targets.gedit.enable) { + xdg.dataFile = { + "gedit/styles/stylix.xml".source = style; + }; + }; +} diff --git a/modules/gedit/template.mustache b/modules/gedit/template.mustache new file mode 100644 index 0000000..e8a4ed0 --- /dev/null +++ b/modules/gedit/template.mustache @@ -0,0 +1,117 @@ + + + + Stylix + <_description>Theme configured as part of your NixOS configuration. + + + + + + + + + + + + + + + + + + + + + +

Primary colors

+
+
00
+
01
+
02
+
03
+
04
+
05
+
06
+
07
+
+ +

Accents

+
+
08
+
09
+
0A
+
0B
+
0C
+
0D
+
0E
+
0F
+
+ +

Documentation

+

Each color should be used as described in this table.

+

See the Stylix documentation for how to apply these colors on NixOS.

+ + diff --git a/stylix/palette.json.mustache b/stylix/palette.json.mustache new file mode 100644 index 0000000..e8dde34 --- /dev/null +++ b/stylix/palette.json.mustache @@ -0,0 +1,21 @@ +{ + "base00": "{{{base00}}}", + "base01": "{{{base01}}}", + "base02": "{{{base02}}}", + "base03": "{{{base03}}}", + "base04": "{{{base04}}}", + "base05": "{{{base05}}}", + "base06": "{{{base06}}}", + "base07": "{{{base07}}}", + "base08": "{{{base08}}}", + "base09": "{{{base09}}}", + "base0A": "{{{base0A}}}", + "base0B": "{{{base0B}}}", + "base0C": "{{{base0C}}}", + "base0D": "{{{base0D}}}", + "base0E": "{{{base0E}}}", + "base0F": "{{{base0F}}}", + "scheme": "Stylix", + "author": "Stylix", + "slug": "stylix" +} diff --git a/stylix/palette.nix b/stylix/palette.nix new file mode 100644 index 0000000..af6cfe1 --- /dev/null +++ b/stylix/palette.nix @@ -0,0 +1,170 @@ +{ + palette-generator, + base16, +}: { + pkgs, + lib, + config, + ... +}: let + cfg = config.stylix; + + paletteJSON = let + generatedJSON = pkgs.runCommand "palette.json" {} '' + ${palette-generator}/bin/palette-generator \ + "${cfg.polarity.force}" \ + "${cfg.polarity.primaryScale.dark}" \ + "${cfg.polarity.primaryScale.light}" \ + ${lib.escapeShellArg "${cfg.image}"} \ + "$out" + ''; + palette = lib.importJSON generatedJSON; + scheme = base16.mkSchemeAttrs palette; + json = scheme { + template = ./palette.json.mustache; + extension = ".json"; + }; + in + json; + generatedScheme = lib.importJSON paletteJSON; +in { + options.stylix = { + polarity = { + force = lib.mkOption { + type = lib.types.enum ["either" "light" "dark"]; + default = "either"; + description = '' + Use this option to force a light or dark theme. + + By default we will select whichever is ranked better by the genetic + algorithm. This aims to get good contrast between the foreground and + background, as well as some variety in the highlight colours. + ''; + }; + + primaryScale = { + dark = lib.mkOption { + type = lib.types.number; + default = 1; + description = '' + Use this option to scale color values for the generated light theme. + ''; + }; + light = lib.mkOption { + type = lib.types.number; + default = 1; + description = '' + Use this option to scale color values for the generated light theme. + ''; + }; + }; + }; + + image = lib.mkOption { + type = with lib.types; coercedTo package toString path; + description = '' + Wallpaper image. + + This is set as the background of your desktop environment, if possible, + and used to generate a colour scheme if you don't set one manually. + ''; + }; + + imageScalingMode = lib.mkOption { + type = lib.types.enum ["stretch" "fill" "fit" "center" "tile"]; + default = "fill"; + description = '' + Wallpaper scaling mode; + + This is the scaling mode your wallpaper image will use assuming it + doesnt fix your monitor perfectly + ''; + }; + + generated = { + json = lib.mkOption { + type = lib.types.path; + description = "The result of palette-generator."; + readOnly = true; + internal = true; + default = paletteJSON; + }; + + palette = lib.mkOption { + type = lib.types.attrs; + description = "The imported json"; + readOnly = true; + internal = true; + default = generatedScheme; + }; + + fileTree = lib.mkOption { + type = lib.types.raw; + description = "The files storing the palettes in json and html."; + readOnly = true; + internal = true; + }; + }; + + base16Scheme = lib.mkOption { + description = '' + A scheme following the base16 standard. + + This can be a path to a file, a string of YAML, or an attribute set. + ''; + type = with lib.types; oneOf [path lines attrs]; + default = generatedScheme; + defaultText = lib.literalMD '' + The colors used in the theming. + + Those are automatically selected from the background image by default, + but could be overridden manually. + ''; + }; + + override = lib.mkOption { + description = '' + An override that will be applied to stylix.base16Scheme when generating + config.lib.stylix.colors. + + Takes anything that a scheme generated by base16nix can take as argument + to override. + ''; + type = lib.types.attrs; + default = {}; + }; + }; + + config = { + # This attrset can be used like a function too, see + # https://github.com/SenchoPens/base16.nix/blob/b390e87cd404e65ab4d786666351f1292e89162a/README.md#theme-step-22 + lib.stylix.colors = (base16.mkSchemeAttrs cfg.base16Scheme).override cfg.override; + lib.stylix.scheme = base16.mkSchemeAttrs cfg.base16Scheme; + + stylix.generated.fileTree = { + # Making palette.json part of the system closure will protect it from + # garbage collection, so future configurations can be evaluated without + # having to generate the palette again. The generator is not kept, only + # the palette which came from it, so this uses very little disk space. + # The extra indirection should prevent the palette generator from running + # when the theme is manually specified. generated.json is necessary in + # the presence of overrides. + "stylix/generated.json".source = config.lib.stylix.scheme { + template = ./palette.json.mustache; + extension = ".json"; + }; + + "stylix/palette.json".source = config.lib.stylix.colors { + template = ./palette.json.mustache; + extension = ".json"; + }; + + # We also provide a HTML version which is useful for viewing the colors + # during development. + "stylix/palette.html".source = config.lib.stylix.colors { + template = ./palette.html.mustache; + extension = ".html"; + }; + }; + }; +} diff --git a/stylix/pixel.nix b/stylix/pixel.nix new file mode 100644 index 0000000..7c10492 --- /dev/null +++ b/stylix/pixel.nix @@ -0,0 +1,9 @@ +{ pkgs, config, ... }: + +{ + # Generate a PNG image containing a named color + config.lib.stylix.pixel = color: + pkgs.runCommand "${color}-pixel.png" { + color = config.lib.stylix.colors.withHashtag.${color}; + } "${pkgs.imagemagick}/bin/convert xc:$color png32:$out"; +} diff --git a/stylix/target.nix b/stylix/target.nix new file mode 100644 index 0000000..c1908da --- /dev/null +++ b/stylix/target.nix @@ -0,0 +1,48 @@ +{ config, lib, ... }: + +{ + options.stylix = { + enable = lib.mkOption { + description = '' + Whether to enable Stylix. + + When this is `false`, all theming is disabled and all other options + are ignored. + ''; + type = lib.types.bool; + default = false; + example = true; + }; + + autoEnable = lib.mkOption { + description = '' + Whether to enable targets by default. + + When this is `false`, all targets are disabled unless explicitly enabled. + + When this is `true`, most targets are enabled by default. A small number + remain off by default, because they require further manual setup, or + they are only applicable in specific circumstances which cannot be + detected automatically. + ''; + type = lib.types.bool; + default = true; + example = false; + }; + }; + + config.lib.stylix.mkEnableTarget = let + cfg = config.stylix; + in + humanName: + autoEnable: + lib.mkEnableOption + "theming for ${humanName}" + // { + default = cfg.enable && cfg.autoEnable && autoEnable; + example = !autoEnable; + } + // lib.optionalAttrs autoEnable { + defaultText = lib.literalMD "same as [`stylix.autoEnable`](#stylixautoenable)"; + }; +} diff --git a/stylix/templates.nix b/stylix/templates.nix new file mode 100644 index 0000000..a3457d0 --- /dev/null +++ b/stylix/templates.nix @@ -0,0 +1,14 @@ +inputs: +{ + config.lib.stylix.templates = { + inherit (inputs) + base16-fish + base16-helix + base16-vim + gnome-shell + tinted-foot + tinted-kitty + tinted-tmux + ; + }; +} diff --git a/stylix/testbed.nix b/stylix/testbed.nix new file mode 100644 index 0000000..c5b7c1a --- /dev/null +++ b/stylix/testbed.nix @@ -0,0 +1,107 @@ +{ pkgs, inputs, lib, ... }: + +let + username = "guest"; + + commonModule = { config, ... }: { + users.users.${username} = { + description = "Guest"; + hashedPassword = ""; + isNormalUser = true; + extraGroups = [ "wheel" ]; + }; + + security.sudo.wheelNeedsPassword = false; + + # The state version can safely track the latest release because the disk + # image is ephermal. + system.stateVersion = config.system.nixos.release; + home-manager.users.${username}.home.stateVersion = config.system.nixos.release; + + virtualisation.vmVariant.virtualisation = { + # This is a maximum limit; the VM should still work if the host has fewer cores. + cores = 4; + memorySize = lib.mkDefault 2048; + }; + }; + + autoload = builtins.concatLists + (lib.mapAttrsToList + (name: _: + let testbed = { + inherit name; + module = "${inputs.self}/modules/${name}/testbed.nix"; + }; + in + lib.optional (builtins.pathExists testbed.module) testbed + ) + (builtins.readDir "${inputs.self}/modules")); + + makeTestbed = + testbed: stylix: + let + name = "testbed-${testbed.name}-${stylix.polarity}"; + + system = lib.nixosSystem { + inherit (pkgs) system; + + modules = [ + commonModule + inputs.self.nixosModules.stylix + inputs.home-manager.nixosModules.home-manager + testbed.module + + { + inherit stylix; + system.name = name; + } + ]; + }; + + script = pkgs.writeShellApplication { + inherit name; + text = '' + cleanup() { + if rm --recursive "$directory"; then + printf '%s\n' 'Virtualisation disk image removed.' + fi + } + + # We create a temporary directory rather than a temporary file, since + # temporary files are created empty and are not valid disk images. + directory="$(mktemp --directory)" + trap cleanup EXIT + + NIX_DISK_IMAGE="$directory/nixos.qcow2" \ + ${lib.getExe system.config.system.build.vm} + ''; + }; + in + lib.nameValuePair name script; + + # This generates a copy of each testbed for each of the following themes. + makeTestbeds = testbed: map (makeTestbed testbed) [ + { + enable = true; + image = pkgs.fetchurl { + name = "three-bicycles.jpg"; + url = "https://unsplash.com/photos/hwLAI5lRhdM/download?ixid=M3wxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNzE2MzYxNDcwfA&force=true"; + hash = "sha256-S0MumuBGJulUekoGI2oZfUa/50Jw0ZzkqDDu1nRkFUA="; + }; + base16Scheme = "${pkgs.base16-schemes}/share/themes/catppuccin-latte.yaml"; + polarity = "light"; + } + { + enable = true; + image = pkgs.fetchurl { + name = "mountains.jpg"; + url = "https://unsplash.com/photos/ZqLeQDjY6fY/download?ixid=M3wxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNzE2MzY1NDY4fA&force=true"; + hash = "sha256-Dm/0nKiTFOzNtSiARnVg7zM0J1o+EuIdUQ3OAuasM58="; + }; + base16Scheme = "${pkgs.base16-schemes}/share/themes/catppuccin-macchiato.yaml"; + polarity = "dark"; + } + ]; + +in + lib.listToAttrs (lib.flatten (map makeTestbeds autoload))