JustToThePoint English Website Version
JustToThePoint en español
Colaborate with us

NixOS X. Hyprland

Sometimes people don’t want to hear the truth because they don’t want their illusions destroyed, Friedrich Nietzsche.

Introduction to NixOS

NixOS is a unique, innovative, and powerful Linux distribution that leverages the Nix package manager. Unlike traditional Linux distributions that update packages and system configurations in-place, NixOS uses a purely functional and declarative approach to define the system state, reducing the risk of system breakage.

NixOS is a Linux distribution that uses the Nix package manager to handle packages and system configuration in a purely functional manner. This approach ensures that builds are reproducible and that the system state can be reliably replicated or rolled back.

  1. Immutable Design: It uses a configuration file (usually /etc/nixos/configuration.nix) to define the entire system state, including installed packages, services, and system settings. This makes the system “immutable” in the sense that you don’t manually modify system files. Instead, you declare what you want, and NixOS builds the system from that declaration.
  2. Atomic Updates: When you run nixos-rebuild switch, the system builds a new generation of the environment. If the build is successful, you can switch to this new environment atomically. This means that either the entire update is applied, or nothing changes at all, preventing partial updates that could leave the system in an inconsistent state: sudo nixos-rebuild switch, build and switch to the new generation. If anything goes wrong, you can easily roll back to a previous generation: sudo nix-env ‐‐rollback This rollback is seamless because each generation is stored separately.

    sudo nix-env ‐‐rollback is a command used to revert your system to the previous configuration. It’s a powerful tool for undoing unintended changes or recovering from failed package installations. Before rolling back, consider if there’s a more targeted solution, like uninstalling specific packages or reverting configuration files.

  3. Purely Functional System Configuration: NixOS uses a functional paradigm for configuration. This means that changes are expressed as pure functions from a configuration to a system state, ensuring reproducible builds and easy rollback.

Hyprland

Hyprland

Hyprland is a modern, dynamic tiling Wayland compositor that offers a highly customizable and modern desktop experience. In NixOS, you can install and configure Hyprland declaratively, allowing you to manage your system configuration (such as display managers, NVIDIA drivers, user permissions, and sound servers) in a reproducible manner from a single or a few .nix files.

System configuration

We need to modify our main configuration NixOS file, vim /etc/nixos/configuration.nix.

Kernel Parameters (NVIDIA)

boot = {
  [...]
  # Kernel parameters for NVIDIA driver support
  kernelParams = [
    "nvidia-drm.modeset=1" # Enables kernel modesetting for the proprietary NVIDIA driver.
    "nouveau.modeset=0" # Disables modesetting for the open-source Nouveau driver, preventing conflicts with proprietary NVIDIA drivers.
  ];
};

GNOME Keyring Configuration

The GNOME Keyring is a secure storage for passwords, encryption keys, and other secrets. By enabling it, users can automatically unlock their keyring upon logging in, allowing seamless access to saved credentials (like Wi-Fi passwords, application passwords, etc.) without needing to enter them again.

# Enable GNOME Keyring for managing secrets
services.gnome.gnome-keyring.enable = true;
security.pam.services.greetd.enableGnomeKeyring = true;

The second line integrates GNOME Keyring with the greetd login manager via PAM (Pluggable Authentication Module, a framework that manages authentication on Unix-like systems) configuration. It ensures that the keyring is unlocked automatically when a user logs in, providing seamless access to stored credentials without needing to re-enter them.

Greetd Display Manager

greetd is a minimal, agnostic, and flexible login manager. It is particularly suitable for Wayland compositors like Hyprland.

# Configure Greetd for managing the display manager
services.greetd = {
  enable = true; # Enables the greetd display manager service
  settings = {
    # This block defines the default session settings for greetd.
    default_session = {
    # This line specifies the command that greetd will run.
    # Tells greetd to launch tuigreet, a visually appealing login screen with customizable themes and colors, ...
    # display current time (--time) and start Hyprland when a user logs in (--cmd Hyprland).
      command = "${pkgs.greetd.tuigreet}/bin/tuigreet --time --cmd Hyprland";
      user = "YOUR-USERNAME"; # This specifies the username that will be used to log into the session by default.
    };
  };
};

NVIDIA Driver Setup

# NVIDIA hardware configuration
hardware.nvidia = {
  modesetting.enable = true; # Enable modesetting for NVIDIA
  # It matches the earlier kernelParams for NVIDIA modesetting.
  open = false; # Indicates that proprietary NVIDIA drivers should be used instead of the open-source alternatives
  nvidiaSettings = true;
  # Enable the NVIDIA settings utility (nvidia-settings) for advanced driver configuration
};

# Enable Bluetooth support
hardware.bluetooth.enable = true;

# Set the LD_LIBRARY_PATH for OpenGL applications
hardware.opengl.setLdLibraryPath = true;

# Specify the video drivers for the X server
services.xserver.videoDrivers = [ "nvidia" ]; # Use the NVIDIA video driver

PipeWire Configuration

Pipewire is a versatile multimedia framework designed to handle audio and video streams on Linux. We are going to enable sound support via PipeWire and disable PulseAudio to avoid conflicts.

# Enable sound support in NixOS
sound.enable = true;

# Disable PulseAudio, as we will use PipeWire instead
hardware.pulseaudio.enable = false;
hardware.pulseaudio.support32Bit = false; # Disable 32-bit support for PulseAudio

# Configure PipeWire for sound management
services.pipewire = {
  enable = true; # Enable PipeWire as the main audio server
  alsa.enable = true; # Enable ALSA support for low-level audio device handling
  pulse.enable = true; # Enable PulseAudio compatibility layer
  wireplumber.enable = true; # Enable WirePlumber, a session manager for PipeWire
};

User Permissions and Groups

The input group provides the necessary permissions for users to interact with input devices like keyboards, mouse, and touchscreens. Hyprland, being a Wayland compositor, directly interacts with these devices to handle input events. Without the user being in the “input” group and lack appropriate permissions, Hyprland won’t be able to receive input from these devices, rendering the system useless.

users.users.YOUR-USERNAME = {
  isNormalUser = true; # Set this user as a normal user
  description = "YOUR-USERNAME"; # Description for the user account
  extraGroups = [ "networkmanager" "wheel" "docker" "input" ]; # Add user to additional groups for permissions
  shell = "${pkgs.zsh}/bin/zsh"; # Set the default shell for the user to Zsh
  packages = with pkgs; [ # Additional packages installed only for this user, e.g., keepassxc, cowsay, etc.
  ];
};

System Packages

# Define system-wide packages to be installed
environment.systemPackages = with pkgs; [
  [...]
  pavucontrol # GUI for controlling PulseAudio settings
  kitty # A GPU-accelerated terminal emulator
  rofi # Application launcher
  rofi-calc # Calculator integration for Rofi
  rofi-power-menu # Power menu for Rofi
  gnupg # GNU Privacy Guard for encryption
  pinentry # PIN entry dialog for GnuPG
  hyprland # The Hyprland compositor
  xorg.xkeyboardconfig # Keyboard configuration for X11
  hyprpaper # Wallpaper manager for Hyprland
  polkit_gnome # PolicyKit integration for GNOME
  waybar # A bar for Wayland (like Polybar but for Wayland)
  psmisc # Package includes utilities like 'killall'
  xorg.xkill # Utility to kill X11 clients
  swww # Simple wallpaper setter
  networkmanagerapplet # Applet for NetworkManager
  xwayland # X compatibility layer for Wayland
  mesa # OpenGL implementation
  gnome.adwaita-icon-theme # Default GNOME icon theme (so flatpak may play "nice")
  gtk3 # GTK 3 libraries
  power-profiles-daemon # Manage power profiles
];

Other Key Services (RTKit, D-Bus)

# Enable RTKit for real-time audio processing, ...
# improving audio performance and reducing dropouts.
security.rtkit.enable = true;
# Enable D-Bus for inter-process communication
services.dbus.enable = true;

D-Bus is an inter-process communication system that allows applications to communicate with one another, e.g., xdg-desktop-portal-hyprland is a D-Bus service that provides portals for applications to interact with the desktop environment in a standardized way.

Home Manager Configuration

Home Manager is a separate tool that manages user-specific configuration (e.g., dotfiles, user-level services and packages, user environment) within NixOS. You typically define user-based settings in something like ~/dotfiles/home-manager.nix.

We need to configure our home-manager.nix configuration file, vim ~/dotfiles/home-manager.nix.

{ config, pkgs, ... }:

let
  username = "nmaximo7"; # Define the username for the Home Manager configuration
  dotfiles = "/home/${username}/dotfiles"; # Path to the user's dotfiles directory
in
{
  # Enable Home Manager features
  home-manager.useGlobalPkgs = true; # Allows usage of globally defined packages in your user environment.
  home-manager.useUserPackages = true;
  # Tell Home Manager to handle user-specific packages
  home-manager.backupFileExtension = "backup";
  # Set the file extension for backups
  # If Home Manager sees conflicting files when deploying new dotfiles,...
  # it appends the extension .backup to old configuration files instead of overwriting them silently.

Troubleshooting and Logs

When things go terrible wrong, our system break, and we cannot rebuild our system, there are a few tools to our disposal:

  1. To gain some insight into why the build is failing, the option show-trace will provide a more detailed trace of the error: sudo nixos-rebuild switch ‐‐show-trace
  2. Read your logs via journalctl, e.g., journalctl -u home-manager-your-user.service -b is used to view (or filter) logs related to the home-manager-your-user service from the current boot (boot) or journalctl -u hyprland.service -b, filter logs specifically for Hyprland’s systemd service from the current boot session.
  3. The home-manager.backupFileExtension option in used to handle backup files. When Home Manager updates a file that conflicts with an existing one (in the user’s home directory), instead of overwriting these files directly, Home Manager will rename the old file by appending .backup, e.g., ~/.config/pcmanfm/default/pcmanfm.conf –> ~/.config/pcmanfm/default/pcmanfm.conf.backup. Sometimes, you will need to delete these files: rm ~/.config/pcmanfm/default/pcmanfm.conf.backup.
    The journalctl -xe command is used to view the system logs, specifically focusing on the most recent and relevant entries and providing detailed information about errors and warnings.

Mako

Mako is a lightweight, customizable, notification daemon for GNU/Linux that is designed to display notifications in a modern and visually appealing manner. It is often used in tiling window managers —like Hyprland, sway, or i3— where users prefer a simple and effective way to receive notifications without the overhead of larger desktop environments such as Gnome or Plasma.

  1. First install the packages: mako (mako notification daemon) and (libnotify, a library that provides a way for applications to send notifications to Mako).

  2. To configure Mako using Home Manager (a framework for managing user-specific configuration, including user services like Mako), you can use the following settings in your Home Manager configuration file (home.nix):

    # Let Home Manager install and manage itself
    programs.home-manager.enable = true;
    
    services.mako = {
      enable = true; # Enables the Mako notification service
      defaultTimeout = 5000; # Default timeout for notifications in milliseconds (5 seconds)
      extraConfig = '' # A multi-line string that defines additional settings for Mako’s appearance.
        background-color=#2e3440 # Sets the background color of notifications
        width=300 # Sets the width of the notifications
        height=110 # Sets the height of the notifications
        border-size=2 # Sets the border size of notifications
        border-color=#88c0d0 # Sets the border color of notifications
        border-radius=15 # Rounds the corners of the notification
        # These settings are for styling a neat border around the notifications with rounded corners
        icons=1 # Enables icons in notifications
        max-icon-size=64 # Sets the maximum size for icons
      '';
    };
    
  3. Open a terminal, rebuild your system (using home-manager switch or nixox-rebuild switch), and send a “hello world” notification: notify-send "Testing" "hello world!"

Home Manager Configuration (continue…)

  # User-specific Home Manager configuration
  home-manager.users.nmaximo7 = { pkgs, ... }:
  {
    # Let Home Manager install and manage itself
    programs.home-manager.enable = true;
    [...]
    # Include other configuration files
    imports = [
          "${dotfiles}/shell.nix"
          "${dotfiles}/kitty.nix"
          "${dotfiles}/zsh.nix"
          "${dotfiles}/alacritty.nix"
          "${dotfiles}/rofi.nix"
          "${dotfiles}/vscode.nix"
          "${dotfiles}/nvim.nix"
          "${dotfiles}/Hyprland/Hyprland.nix"
          # Import Hyprland specific configuration
        # Add other configuration files as needed
    ];

    # Define a global override for Flatpak applications to see (and share) fonts from your Nix store
    home.file.".local/share/flatpak/overrides/global".text = ''
      [Context]
      filesystems=/run/current-system/sw/share/X11/fonts:ro;/nix/store:ro
    '';
  };
}

Hyprland.nix

Let’s create a new configuration file for our tiling Wayland compositor, vim ~/dotfiles/Hyprland.nix. This file defines our main Hyprland configuration module in NixOS. It ensures that Hyprland is enabled, sets up XWayland support, and references additional modular files like keybindings, window rules, environment variables, decorations, and animations.

{ config, pkgs, lib, ... }:

let
  username = "YOUR-USERNAME"; # Define the username for the configuration
  browser = "${pkgs.firefox}/bin/firefox"; # Path to the browser executable (Firefox)
  terminal = "${pkgs.kitty}/bin/kitty"; # Path to the terminal executable (Kitty)
  home = config.home; # Reference to the home configuration
  scriptsDir = "/home/${username}/dotfiles/scripts"; # Directory containing user's scripts
  powerMenuCommand = "rofi -show power-menu -modi power-menu:rofi-power-menu"; # Command for power menu
  # Import keybindings from a separate file for modularity
  keybindings = import /home/${username}/dotfiles/keybindings.nix {
    modifier = "SUPER";  # Modifier key for keybindings
    terminal = terminal;  # Reference to the terminal
    browser = browser;  # Reference to the browser
    scriptsDir = scriptsDir;  # Reference to the scripts directory
    powerMenuCommand = powerMenuCommand;  # Reference to power menu command
  };
in
{
  # Enables Hyprland with XWayland support and specifies the package to use.
  wayland.windowManager.hyprland = {
    enable = true; # Enable the Hyprland window manager under Wayland.
    xwayland = {
      enable = true; # Enable XWayland support for X11 applications
    };
    systemd.enable = true; # Enable systemd integration
    package = pkgs.hyprland; # Specify the package to use

    extraConfig = let
      modifier = "SUPER"; # Define the modifier key (usually the Windows key)
    in ''
      # Monitor configuration
      monitor=DVI-I-1,1920x1080@60,auto,1
❯ hyprctl monitors
# This command provides detailed information about all available monitors
# This information includes identifiers, resolutions, positions, and other properties.
Monitor DVI-I-1 (ID 0):
	1920x1080@60.00000 at 0x0
	description: LG Electronics LG TV SSCR2 0x01010101
	[...]
  availableModes: 3840x2160@30.00Hz ... 1920x1080@50.00Hz 1920x1080@29.97Hz ... 640x480@59.94Hz

We continue editing our file, vim ~/dotfiles/Hyprland.nix.

      # Import environment variables on session start
      ${import /home/${username}/dotfiles/Hyprland/env.nix {}}

      # It updates the systemd user environment with variables needed for Wayland and your desktop environment.
      # Ensure that applications and services running in your user session have the necessary environment variables set.
      exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
      # It runs the NetworkManager tray icon.
      exec-once = nm-applet --indicator
      # It launches Waybar after a 1-second delay.
      exec-once = sleep 1 && waybar
      # It opens Alacritty on workspace 1 with a custom message.
      exec-once = [workspace 1 silent] alacritty -e sh -c 'cowsay "Welcome master! Type inicio"; exec $SHELL'
      # Each ${import ...} statement loads a separate Nix file, which allows you to keep different parts of your configuration modular. This is beneficial for organization and ease of maintenance.

      # Window rules
      ${import /home/${username}/dotfiles/Hyprland/windowrules.nix {}}

      # Visual customization
      ${import /home/${username}/dotfiles/Hyprland/visual.nix {}}

      # Import animations
      ${import /home/${username}/dotfiles/Hyprland/animations.nix {}}

      # Decorations
      ${import /home/${username}/dotfiles/Hyprland/decorations.nix {}}

      # Importing your keybindings. It allows you to manage your keybindings independently.
      ${keybindings}

    '';
  };
}

env.nix: This file sets environment variables required for proper Wayland operation, specifically for certain applications to be able to detect my environment, basically they should run in Wayland mode.

{ }:

''
# Sets necessary environment variables for Wayland and application compatibility.
env = XDG_CURRENT_DESKTOP,Hyprland
# Tells apps you're running Hyprland. Some applications look for XDG_CURRENT_DESKTOP to detect your environment and adapt behavior.
env = XDG_SESSION_TYPE,wayland # Indicates a Wayland session
env = GDK_BACKEND,wayland # It ensures GTK applications use Wayland.
env = QT_QPA_PLATFORM,wayland # It ensures Qt applications use Wayland.
env = MOZ_ENABLE_WAYLAND,1 # It helps Firefox and other Mozilla-based apps run natively on Wayland.
env = GDK_SCALE,1
env = QT_SCALE_FACTOR,1
# GDK_SCALE and QT_SCALE_FACTOR help scale apps correctly on high DPI or scaled displays.
''

windowrules.nix. The rules for how specific applications are handled, such as assigning them to certain workspaces or making them float. The file references windowrulev2, which is a Hyprland directive for handling windows.


{ }:

''
  # Assign applications to specific workspaces
  windowrulev2 = workspace 1, class:^(Alacritty)$
  # It places Alacritty windows on workspace 1, firefox on workspace 2, and so on.
  windowrulev2 = workspace 2, class:^(firefox)$
  windowrulev2 = workspace 3, class:^(obsidian)$
  windowrulev2 = workspace 4, class:^(code-oss)$
  windowrulev2 = workspace 4, class:^(Google-chrome)$
  windowrulev2 = workspace 5, class:^(pcmanfm)$
  windowrulev2 = workspace special:geany, class:^(Geany)$

  # Float VLC and pavucontrol, meaning VLC and pavucontrol...
  # will always be a floating window, rather than tiling with other apps.
  windowrulev2 = float, class:^(vlc)$
  windowrulev2 = float, class:^(pavucontrol)$

  # Float Rofi windows
  windowrulev2 = float, class:^(Rofi)$
''

This instruction has three elements:

  1. windowrule(v2) defines a rule for window behavior.
  2. float specifies the action. We want to automatically make some windows float. They can be moved around freely and don't tile with other windows. This could be really useful for application where we want or need a more flexible positioning for our applications.
  3. Define the application classes, e.g., vlc and pavucontrol.

Special work spaces (scratchpad)

Hyprland allows you the ability to set what is called a “scratchpad” or “special workspace”. This is a special, nifty little workspace, effectively acting like a scratchpad (virtual notepad) that can be called from any workspace that you’re currently on. It is basically a workspace that you can toggle on/off on any monitor or workspace. This could be useful for organizing your workflow by keeping an application, such as code editors (Geany), communication tools (Ferdium), or note-taking apps (Obsidian), readily accessible without cluttering your main workspace and enhancing your productivity.

  1. Define the window rule (windowrules.nix): windowrulev2 = workspace special:geany, class:^(Geany)$. It specifies that geany (window’s class matching the regex ^(Geany)$) should be directed or routed to the special workspace named “geany.” This allows you to isolate Geany in its own workspace.
  2. Set up the keybinding to toggle the special workspace: (keybindings.nix): bind = ${modifier},G,togglespecialworkspace,geany. It allows you to toggle the visibility of the special workspace “geany” on and off using a combination of a modifier key (e.g., Ctrl, Alt) and the key “G.” This makes it convenient to switch to the workspace quickly and efficiently.
  3. Launch Geany in the special workspace (~/dotfiles/scripts/inicio.nix): launch_app_in_workspace "special:geany" "geany".

visual.nix. This file specifies general aesthetics and input settings for Hyprland, such as gaps, borders, input sensitivity, and more.

{ }:
''
# General Settings
general {
        gaps_in = 6
        # gaps_in and gaps_out control the space within and around tiled windows.
        gaps_out = 8
        # This specifies the thickness of the window borders.
        border_size = 2
        layout = dwindle # uses a spiral/dwindle tiling layout.
        # The dwindle layout is a dynamic tiling layout that allows windows to dynamically resize, move, and reorganize, providing a flexible workspace.
        resize_on_border = true
        # It allows you to resize windows by dragging their borders, which can enhance usability.
}

# Input Settings
input {
    kb_layout = es, us
    # It sets Spanish and US English keyboard layouts, allowing for quick switching.
    kb_options = "grp:alt_shift_toggle,caps:super"
    # The options specify how to toggle keyboard layouts (by pressing Alt + Shift) and set the Caps Lock key to act as a Super key
    sensitivity = 0.0
    # Setting the sensitivity to 0.0 might indicate that you want to disable any pressure sensitivity for input devices, such as touchpads or styluses.
    accel_profile = "flat"
    # This setting indicates a linear acceleration profile for mouse movement, providing consistent pointer speed, which can be beneficial for precision tasks.
}

# Misc settings
misc {
        initial_workspace_tracking = 0
        # This controls the initial tracking of the workspace. A value of 0 mean no initial tracking is set, allowing for a fresh start.
        mouse_move_enables_dpms = true
        # Setting this to true means that moving the mouse will wakes the display after power-saving mode,...
        # which can help save energy when the display is not in use.
        key_press_enables_dpms = false
        # This setting disables DPMS activation (power-saving mode) on key presses.
}
''

animations.nix. Controls how windows appear, disappear, or move with animations. Greatly affects the look-and-feel of Hyprland.

# -----------------------------------------------------
# Animations Configuration
# -----------------------------------------------------
{ ... }:

''
animations {
    enabled = true;  # Enable animations
    bezier = myBezier, 0.05, 0.9, 0.1, 1.05;
    # Defines a custom bezier curve to shape the animation’s movement for windows and transitions.

    # Define different window animations
    animation = windows, 1, 7, myBezier;  # Transitions for windows appearing.
    # The numeric parameters (1, 7, etc.) affect timing and animation speed.
    animation = windowsOut, 1, 7, default, popin 80%;  # Transitions for windows disappearing
    animation = border, 1, 10, default;  # Animation for border changes
    animation = borderangle, 1, 8, default;  # Animation for border angle changes
    animation = fade, 1, 7, default;  # Fade animation
    animation = workspaces, 1, 6, default;  # Animation for switching workspaces
}
''

keybinding.nix: Your custom bindings to launch apps, manage windows and workspaces, take screenshots, etc. This file references inputs from the main Hyprland.nix.

{ modifier, terminal, browser, scriptsDir, powerMenuCommand, ... }:
let
  screenshotDir = "$HOME/Downloads";
in

''
# Keybindings for launching applications, moving windows, and managing workspaces.
# ${modifier} usually references SUPER or the "Windows" key.
# Launching Apps ------------------------------
bind = ${modifier},Return,exec,${terminal} # Open terminal with Windows (Modifier) + Return.
bind = ${modifier},W,exec,${browser} # Open browser (Firefox) with Windows + W
bind = ${modifier},F1,exec,alacritty # Launch Alacritty terminal with Windows + F1
bind = ${modifier},F2,exec,firefox # Launch Firefox with Windows + F2
bind = ${modifier},E,exec,sh /home/${username}/dotfiles/scripts/empezar.sh # Custom script
# Window/Session Management ------------------------------
bind = ${modifier},M,exit # forcibly closes the entire Hyprland session
bind = ${modifier},V,togglefloating # It switches the current window between tiled (normal) and floating (free-moving) modes.
# Rofi ------------------------------
bind = ${modifier},D,exec,rofi -combi-modi window,drun,ssh ,emoji -font "hack 10" -show combi -show-icons
# Launch Rofi with the combined modes specified (window, drun, ssh, emoji)...
#  for launching programs, switching windows, or picking emojis and displays icons next to the entries.

bind = ${modifier}+SHIFT,D,exec,rofi -show power-menu --choices=shutdown/reboot/logout/lockscreen --confirm=logout/lockscreen
# Launch Rofi power menu. It prompts for confirmation ONLY before executing the logout or lock screen actions.
bind = ${modifier},Q,killactive # Kill active window
bind = ${modifier}+CTRL,Q,exec,hyprctl dispatch exit # Exit Hyprland

# Screenshots ------------------------------
bind = ${modifier},P,exec,sh -c 'grim "${screenshotDir}/$(date +%Y-%m-%d_%H-%M-%S).png"'
# It takes a full screenshot with Grim, saving it to $HOME/Downloads.
bind = ${modifier}+ALT,P,exec,sh -c 'grim -g "$(slurp)" "${screenshotDir}/$(date +%Y-%m-%d_%H-%M-%S).png"'
# It takes a region screenshot, letting you select an area with Slurp.
Bitcoin donation

JustToThePoint Copyright © 2011 - 2025 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.