A universal design system for Nix configurations
CrystalNix is a powerful design system that transforms raw theme values into format-specific outputs for any application. Define your colors, spacing, and typography once, then use them everywhere - from Hyprland to CSS to terminal emulators.
- ๐จ Universal Theming: One theme definition works everywhere
- ๐ฑ Multi-Format Output: Colors as hex, conf, RGB; spacing as px, rem, raw
- ๐ง Application-Ready: Built-in support for Hyprland, Kitty, i3, CSS, and more
- ๐งฉ Extensible: Easy to add new formats and transform functions
- ๐ฏ Type-Safe: Schema-driven validation ensures consistent outputs
- โก Flake-Based: Modern Nix flake for easy dependency management
{
inputs.crystalnix.url = "github:you/crystalnix";
outputs = { crystalnix, ... }: {
# Use built-in themes
darkTheme = crystalnix.lib.mkStylesheet { theme = "dark"; };
lightTheme = crystalnix.lib.mkStylesheet { theme = "light"; };
# Or create custom theme
myTheme = crystalnix.lib.mkStylesheet {
theme = "dark";
overrides = {
colors.primary."500" = "#ff6b6b";
};
};
};
}# Colors in different formats
stylesheet.colors.primary."500".hex # "#3b82f6" (CSS)
stylesheet.colors.primary."500".conf # "3b82f6" (Hyprland)
stylesheet.colors.primary."500".rgb # { r = 59; g = 130; b = 246; }
# Spacing in different units
stylesheet.spacing.md.px # "16px"
stylesheet.spacing.md.rem # "1rem"
stylesheet.spacing.md.raw # 16
# Typography formats
stylesheet.typography.fontFamily.sans.css # "Inter, system-ui, ..."
stylesheet.typography.fontFamily.sans.single # "Inter"crystalnix/
โโโ flake.nix # Main flake entry point
โโโ lib/ # Core design system library
โ โโโ default.nix # Main exports (processTheme, etc.)
โ โโโ utils.nix # Helper functions (hex conversion, etc.)
โ โโโ defaults.nix # Configuration defaults
โ โโโ schema.nix # Transform schema mapping
โ โโโ processor.nix # Core theme processor
โ โโโ transforms/ # Transform functions
โ โ โโโ default.nix # Transform registry
โ โ โโโ colors.nix # Color transforms (hex, conf, rgb)
โ โ โโโ spacing.nix # Spacing transforms (px, rem, raw)
โ โ โโโ typography.nix # Typography transforms
โ โ โโโ motion.nix # Motion/timing transforms
โ โ โโโ effects.nix # Effects transforms (opacity, shadows)
โ โโโ themes/ # Built-in themes
โ โโโ default.nix # Theme registry
โ โโโ dark.nix # Dark theme (raw values)
โ โโโ light.nix # Light theme (raw values)
โโโ README.md # This file
Themes contain only raw values - the system handles all format conversions:
# mytheme.nix
{
colors = {
primary = {
"500" = "#3b82f6"; # Raw hex color
};
background = {
primary = "#0f172a";
};
};
spacing = {
md = 16; # Raw pixel value
lg = 32;
};
typography = {
fontFamily = {
sans = ["Inter" "system-ui" "sans-serif"]; # Raw font list
};
fontSize = {
base = 16; # Raw pixel size
};
};
}The system automatically transforms these into:
# After processing
{
colors.primary."500" = {
hex = "#3b82f6";
conf = "3b82f6";
rgb = { r = 59; g = 130; b = 246; };
rgbString = "59,130,246";
};
spacing.md = {
px = "16px";
rem = "1rem";
raw = 16;
};
typography.fontFamily.sans = {
css = "Inter, system-ui, sans-serif";
single = "Inter";
raw = ["Inter" "system-ui" "sans-serif"];
};
}{
general = {
col.active_border = stylesheet.colors.primary."500".conf;
col.inactive_border = stylesheet.colors.border.primary.conf;
border_size = stylesheet.borders.width.normal.raw;
gaps_in = stylesheet.spacing.sm.raw;
gaps_out = stylesheet.spacing.md.raw;
};
decoration = {
rounding = stylesheet.borders.radius.md.raw;
};
}{
background = stylesheet.colors.background.primary.hex;
foreground = stylesheet.colors.text.primary.hex;
cursor = stylesheet.colors.primary."500".hex;
font_family = stylesheet.typography.fontFamily.mono.single;
font_size = stylesheet.typography.fontSize.base.raw;
}:root {
--color-primary: #{stylesheet.colors.primary."500".hex};
--spacing-md: #{stylesheet.spacing.md.px};
--font-family: #{stylesheet.typography.fontFamily.sans.css};
--duration-fast: #{stylesheet.motion.duration.fast.ms};
}{
set = {
"$bg" = stylesheet.colors.background.primary.hex;
"$fg" = stylesheet.colors.text.primary.hex;
"$accent" = stylesheet.colors.primary."500".hex;
};
gaps = {
inner = stylesheet.spacing.sm.raw;
outer = stylesheet.spacing.xs.raw;
};
}# List available themes
nix run .#list-themes
# Debug a theme (shows key values and formats)
nix run .#debug dark
# Compare two themes
nix run .#compare dark light
# Test format outputs
nix run .#formats dark
# Run validation tests
nix run .#validate
# Development shell
nix developdark: Modern dark theme optimized for terminals and low-light uselight: Clean light theme perfect for documentation and professional apps
# Extend a built-in theme
extendedTheme = crystalnix.lib.mkStylesheet {
theme = "dark";
overrides = {
colors.primary."500" = "#ff6b6b"; # Custom accent color
spacing.custom = 48; # Add custom values
};
};
# Or using the helper function
customTheme = crystalnix.lib.designSystem.extendTheme
crystalnix.lib.designSystem.themes.dark
{ colors.primary."500" = "#ff6b6b"; };The transform system automatically converts raw theme values into multiple formats:
hex:"#3b82f6"(CSS standard)conf:"3b82f6"(Config files, Hyprland)rgb:{ r = 59; g = 130; b = 246; }(Object format)rgbString:"59,130,246"(String format)
px:"16px"(CSS pixels)rem:"1rem"(Relative units)raw:16(Bare number)
css:"Inter, system-ui, sans-serif"(CSS font stack)single:"Inter"(Single font for limited configs)raw:["Inter" "system-ui" "sans-serif"](Array format)
ms:"300ms"(CSS milliseconds)s:"0.3s"(CSS seconds)raw:300(Bare number)
# lib/transforms/myTransform.nix
{ lib, utils, defaults }:
{
# Transform function takes raw value, returns format object
myTransform = value: {
raw = value;
custom = "custom-${toString value}";
# ... other formats
};
}# lib/schema.nix
{
# ... existing mappings
myNewSection = "myTransform"; # Apply myTransform to this path
}# themes/cyberpunk.nix
{
colors = {
primary."500" = "#00ff41"; # Matrix green
background.primary = "#0d1421";
# ... rest of theme
};
# ... other theme properties
}processTheme rawTheme: Transform raw theme into stylesheetmkStylesheet { theme, overrides }: Process theme with optional overridesextendTheme baseTheme overrides: Merge themes recursively
themes.dark: Dark theme optimized for terminalsthemes.light: Light theme for documentation/professional use
utils.hexToRgb: Convert hex to RGB componentsutils.toPx: Convert number to pixel stringutils.toRem: Convert pixels to rem unitsutils.fontListToCss: Convert font array to CSS string
- Fork the repository
- Create a feature branch
- Add your changes (transforms, themes, etc.)
- Run validation:
nix run .#validate - Submit a pull request
- Keep themes minimal (raw values only)
- Add transform tests for new formats
- Update documentation for new features
- Follow the existing naming conventions
MIT License - see LICENSE file for details.
- Inspired by design systems like Tailwind CSS and Material Design
- Built with the power and flexibility of Nix
- Thanks to the Nix community for feedback and contributions
๐ฎ Transform your configurations with CrystalNix - one theme, infinite possibilities.