
This article is part of a series about Zsh:
There are many boring tasks we repeat day after day: creating, copying, moving or searching files, launching again and again the same tools, docker containers, and whatnot.
For a developer, the shell is a precious asset which can increase your efficiency over time. It will bring powerful tools at your fingertips, and, more importantly, it will allow you to automate many parts of your workflow.
To leverage these functionalities, you’ll need a powerful and flexible shell. Today, I would like to present your next best friend: the Z shell, or Zsh.
If you look at the documentation (around 450 pages for the PDF version), Zsh can feel daunting. There are so many options available, it can be difficult to come up with a basic configuration you can build upon.
We’ll build, in this article, a basic Zsh config. I’ll explain the meaning of (almost) everything along the way, including:
- What’s a Unix shell.
- Why Zsh is a good choice.
- How to install Zsh.
- A brief overview of:
- Useful environment variables.
- Aliases.
- The Zsh options.
- The Zsh completion.
- The Zsh prompt.
- The Zsh directory stack.
- How to configure Zsh to make it Vim-like.
- How to add external plugins to Zsh.
- External programs you can use to improve your Zsh experience.
Are your keyboard ready? Are you fingers warm? Did you stretch your arms? Let’s begin, then!
Brief Unix Shell Overview
A shell interpret command lines. You can type them using a prompt in an interactive shell, or you can run shell scripts using a non-interactive shell.
The shell run just after you logged in with your user. You can imagine the shell as the layer directly above the kernel of Unix-based operating systems (including Linux). Here’s the charismatic Brian Kernighan explaining it casually with his feet on a table.
When you use a graphical interface (or GUI), you click around with your mouse to perform tasks. When you use a shell, you use plain text instead.
If you use a graphical interface (like a windows manager or a desktop environment), you’ll need a terminal emulator to access the shell. In the old days, a terminal was a real device. Nowadays, it’s a program.
The shell gives you access to many powerful programs. They are called CLIs, or Command Line Interfaces.
At that point, you might wonder: why using a shell, instead of a graphical interface?
- It’s difficult to get a graphical interface right, especially if your software has many functionalities. It can be simpler to build a CLI to avoid some complexity.
- CLIs are usually faster.
- A developer deals often with plain text. CLIs are great for that.
- Many shells, like Linux shells, allow you to pipe CLIs together in order to create a powerful transformation flow.
- It’s easier to automate textual commands rather than actions on a graphical interface.
Play around with your command shell, and you’ll be surprised at how much more productive it makes you.
A shell is the keystone of a Mouseless Development Environment, and the most powerful tool you can use as a developer.
Bash vs Zsh
There are other Linux shells available out there, including the famous Bash. Why using Zsh?
- The level of flexibility and customization of Zsh is crazy.
- You have access to a powerful completion for your favorite CLIs.
- The Vi mode is golden for every Vim lovers.
- There is an important and active community around Zsh.
- Bash scripts are (mostly) compatible with Zsh.
Bash is simpler than Zsh, but it has also less functionalities.
Zsh Without oh-my-zsh
You’ll see many advising you to install a Zsh framework with a crazy number of plugins, options, aliases, all already configured. The famous ones are Oh My Zsh and prezto.
I tried this approach for years and I think the drawbacks outweigh the benefits:
- I have no clue what’s included in these frameworks. When I read their documentations, I can’t possibly remember everything it sets. Therefore, I barely use 10% of the functionalities.
- Zsh has already many functionalities and options, it’s even more daunting to have a framework on top.
- A framework is a big external dependency which brings more complexity. If there is a conflict with my own configuration or a bug, it can take a long time to figure out what’s happening.
- A framework impose rules and way of doing I don’t necessarily want, or need.
Don’t get me wrong: these frameworks are incredible feats. They can be useful to get some inspiration for your own configuration. But I wouldn’t use them directly.
Let The Party Begin
We’ll now configure Zsh. If the files or folders I’m speaking about don’t exist, you need to create them.
This configuration was tested with a Linux based system. I have no idea about macOS, but it should work.
Installing Zsh
You can install Zsh like everything else:
- Debian / Ubuntu:
sudo apt install zsh - Red Hat:
sudo yum install zsh - Arch Linux:
sudo pacman -S zsh - macOS (with brew):
brew install zsh
Then, run it in a terminal by typing zsh.
Zsh Config Files
To configure Zsh for your user’s session, you can use the following files:
In case you wonder what stands for, we’ll come back to it soon.
Zsh read these files in the following order:
.zshenv- Should only contain user’s environment variables..zprofile- Can be used to execute commands just after logging in..zshrc- Should be used for the shell configuration and for executing commands..zlogin- Same purpose than.zprofile, but read just after.zshrc..zlogout- Can be used to execute commands when a shell exit.
We’ll use only .zshenv and .zshrc in this article.
Zsh Config Path
By default, Zsh will try to find the user’s configuration files in the directory. You can change it by setting the environment variable .
Personally, I like to have all my configuration files in . To do so:
- I set the variable
as following:export XDG_CONFIG_HOME="$HOME/.config". - I set the environment variable
:export ZDOTDIR="$XDG_CONFIG_HOME/zsh". - I put the file
.zshrcin thedirectory.
Most software will use the path in to install their own config files. As a result, you’ll have a clean directory.
Unfortunately, the file .zshenv needs to be in your home directory. It’s where you’ll set . Then, every file read after .zshenv can go into your directory.
Zsh Basic Config
Environment Variables
As we saw, you can set the environment variables you need for your user’s session in the file . This file should only define environment variables.
For example, you can set up the XDG Base directory there, as seen above:
export XDG_CONFIG_HOME="$HOME/.config"
+export XDG_DATA_HOME="$XDG_CONFIG_HOME/local/share"
+export XDG_CACHE_HOME="$XDG_CONFIG_HOME/cache"
+You can also make sure that any program requiring a text editor use your favorite one:
export EDITOR="nvim"
+export VISUAL="nvim"
+You can set some Zsh environment variables, too:
export ZDOTDIR="$XDG_CONFIG_HOME/zsh"
+
+export HISTFILE="$ZDOTDIR/.zhistory" # History filepath
+export HISTSIZE=10000 # Maximum events for internal history
+export SAVEHIST=10000 # Maximum events in history file
+I already explained the first line. For the other ones, they will:
- Store your command line history in the file
.zhistory. - Allows you to have a history of 10000 entries maximum.
Here’s my .zshenv file, if you need some inspiration.
Aliases
Aliases are crucial to improve your efficiency. For example, I have a bunch of aliases for git I use all the time. It’s always easier to type when it’s shorter:
alias gs='git status'
+alias ga='git add'
+alias gp='git push'
+alias gpo='git push origin'
+alias gtd='git tag --delete'
+alias gtdr='git tag --delete origin'
+alias gr='git branch -r'
+alias gplo='git pull origin'
+alias gb='git branch '
+alias gc='git commit'
+alias gd='git diff'
+alias gco='git checkout '
+alias gl='git log'
+alias gr='git remote'
+alias grs='git remote show'
+alias glo='git log --pretty="oneline"'
+alias glol='git log --graph --oneline --decorate'
+I like to have my aliases in one separate file (called, surprisingly, aliases), and I source it in my .zshrc:
source /path/to/my/aliases
+Here are all my aliases.
Zsh Options
You can set or unset many Zsh options using setopt or unsetopt. For example:
setopt HIST_SAVE_NO_DUPS # Do not write a duplicate event to the history file.
+unsetopt HIST_SAVE_NO_DUPS # Write a duplicate event to the history file
+You can already do a lot of customization only using these options.
Zsh Completion System
The completion system of Zsh is one of its bigger strength, compared to other shells.
To initialize the completion for the current Zsh session, you’ll need to call the function compinit. More precisely, you’ll need to add this in your zshrc:
autoload -U compinit; compinit
+What does it mean?
The autoload command load a file containing shell commands. To find this file, Zsh will look in the directories of the Zsh file search path, defined in the variable , and search a file called compinit.
When compinit is found, its content will be loaded as a function. The function name will be the name of the file. You can then call this function like any other shell function.
What about the semi-colon ;? It’s just a handy way to separate commands. It’s the same as calling compinit on a new line.
Why using autoload, and not sourcing the file by doing source ~/path/of/compinit?
- It avoids name conflicts if you have an executable with the same name.
- It doesn’t expand aliases thanks to the
-Uoption. - It will load the function only when it’s needed (lazy-loading). It comes in handy to speed up Zsh startup.
Then, let’s add the following;
_comp_options+=(globdots) # With hidden files
+source /my/path/to/zsh/completion.zsh
+The first line will complete dotfiles.
The second line source this file. It’s my personal config for the Zsh completion. I’ve written an article about that if you’re interested to dive more into the completion system.
Now, the completion should work:
- If you type
cpand hit the tab key, you’ll see that Zsh will complete the command. - If you type
cp -and hit the tab key, Zsh will display the possible arguments for the command.

Pimp My Zsh Prompt
What would be the shell experience without a nice prompt? Dull. Tasteless. Depressing.
Let’s be honest here: Zsh default prompt is ugly. We need to change it, before our eyes start crying some blood. My needs are simple:
- The prompt needs to be on one line. I had display problems with two lines.
- The prompt needs to display some git info when necessary.
From there, I created my own prompt from another one. It looks like that:

If you open the prompt script, you’ll see that it’s pretty simple:
- I set two environment variables:
and. The first one format the left prompt, the second display git information on the far right. - You can add some formatting styles using, for example,
%F{blue}%fto change the color, or%Bmy-cool-prompt%bto make everything bold.
This prompt doesn’t need any external dependency. You can copy it right away and modify it as much as you want.
Here’s everything you need, to create the prompt of your dream.
To load the prompt, you need to add something like that in your zshrc:
fpath=(/my/path/to/zsh/prompt $fpath)
+autoload -Uz name_of_the_prompt_file; name_of_the_prompt_file
+The first line will add the folder containing the prompt to , as discussed above. It will also ensure that any function declared in the folder /my/path/to/zsh/prompt will overwrite every other ones with the same name, in other fpath folders.
The second line autoload the prompt itself.
This prompt require font awesome 4 for the git icons. You can download the font and install it, or you can change the icons.
Zsh Directory Stack
Zsh has commands to push and pop directories on a directory stack.
By manipulating this stack, you can set up an history of directory visited, and be able to jump back to these directories.
First, let’s set some options in your .zshrc:
setopt AUTO_PUSHD # Push the current directory visited on the stack.
+setopt PUSHD_IGNORE_DUPS # Do not store duplicates in the stack.
+setopt PUSHD_SILENT # Do not print the directory stack after pushd or popd.
+Then, you can create these aliases:
alias d='dirs -v'
+for index ({1..9}) alias "$index"="cd +${index}"; unset index
+What does it do?
- Every directory visited will populate the stack.
- When you use the alias
d, it will display the directories on the stack prefixed with a number. - The line
for index ({1..9}) alias "$index"="cd +${index}"; unset indexwill create aliases from 1 to 9. They will allow you to jump directly in whatever directory on your stack.
For example, if you execute 1 in Zsh, you’ll jump to the directory prefixed with 1 in your stack list.
You can also increase index ({1..9}) to index ({1..100}) for example, if you want to be able to jump back to 100 directories.
For example, you can do that:
~ > cd .config
+~/.config > cd devdash
+~/.config/devdash > cd ..
+~/.config > cd i3
+~/.config/i3 > cd ..
+~/.config > d
+0 ~/.config
+1 ~/.config/i3
+2 ~/.config/devdash
+3 ~
+~/.config > 2
+~/.config/devdash >
+Zsh By Default
When you’re ready psychologically to set Zsh as your default shell, you can run these commands:
- For Linux:
chsh -s $(which zsh) - For macOS:
sudo sh -c "echo $(which zsh) >> /etc/shells" && chsh -s $(which zsh)
A good soul on Reddit whispered me that Zsh is now the default shell from macOS Catalina onwards, so you don’t necessarily need the above command.
Zsh is now part of your life. Congratulation!
Zsh With Vim Flavors
For editing purposes, Vim is my best friend. I love when CLIs use some Vim key binding, and Zsh gives you even more than that. If you’d like to learn Vim, this series of articles can help.
Activating Vi Mode
Zsh has a Vi mode you can enable by adding the following in your .zshrc:
bindkey -v
+export KEYTIMEOUT=1
+You can now switch between INSERT and NORMAL mode (called also COMMAND mode) with the ESC key, and use the familiar Vim keystrokes to edit what you’re typing in your shell prompt. I write the different modes in uppercase here for clarity, but it doesn’t have to be.
The second line export KEYTIMEOUT=1 makes the switch between modes quicker.
Changing Cursor
A visual indicator to show the current mode (NORMAL or INSERT) could be nice. In Vim, my cursor is a beam | when I’m in INSERT mode, and a block █ when I’m in NORMAL mode. I wanted the same for Zsh.
You can add the following in your zshrc, or autoload it from a file, as I did.
cursor_mode() {
+ # See https://ttssh2.osdn.jp/manual/4/en/usage/tips/vim.html for cursor shapes
+ cursor_block='\e[2 q'
+ cursor_beam='\e[6 q'
+
+ function zle-keymap-select {
+ if [[ ${KEYMAP} == vicmd ]] ||
+ [[ $1 = 'block' ]]; then
+ echo -ne $cursor_block
+ elif [[ ${KEYMAP} == main ]] ||
+ [[ ${KEYMAP} == viins ]] ||
+ [[ ${KEYMAP} = '' ]] ||
+ [[ $1 = 'beam' ]]; then
+ echo -ne $cursor_beam
+ fi
+ }
+
+ zle-line-init() {
+ echo -ne $cursor_beam
+ }
+
+ zle -N zle-keymap-select
+ zle -N zle-line-init
+}
+
+cursor_mode
+You can now speak about beams and blocks with passion and verve.
Vim Mapping For Completion
To give Zsh more of a Vim taste, we can set up the keys hjkl to navigate the completion menu.
First, add the following to your zshrc:
zmodload zsh/complist
+bindkey -M menuselect 'h' vi-backward-char
+bindkey -M menuselect 'k' vi-up-line-or-history
+bindkey -M menuselect 'l' vi-forward-char
+bindkey -M menuselect 'j' vi-down-line-or-history
+We load here the Zsh module complist. Modules have functionalities which are not part of the Zsh’s core, but they can be loaded on demand. Many different modules are available for your needs.
Here, the module complist give you access to the keymap menuselect, to customize the menu selection during completion, including how to select what you want.
In general, the command bindkey -M bind a key to a specific keymap. A keymap is a set of keystrokes bind to specific Zsh functions. In this case, the keymap menuselect bind keystrokes with selecting something in a list.
To list all the keymaps available (depending on the modules you’ve loaded), you can run in your shell bindkey -l (for list). You can also find the default ones here.
Last thing: you should always load the module zsh/complist before autoloading compinit.
Editing Command Lines In Vim
Good news: you can use your favorite editor to edit the commands you’re typing in your prompt! Let’s add these lines in your .zshrc to do so:
autoload -Uz edit-command-line
+zle -N edit-command-line
+bindkey -M vicmd v edit-command-line
+Here, we autoload edit-command-line, a function from the module zshcontrib, which includes many contributions from Zsh users. This specific function let you edit a command line in your visual editor, defined by the environment variable (or ). Great! That’s what we wanted.
We already saw bindkey -M. Using the keymap vicmd, we can bind commands to some NORMAL mode keystrokes. It means that, when you’re in NORMAL mode, you can hit v to directly edit your command in your editor.
Adding Text Objects
If you use the Vi-mode of Zsh for a while, you’ll notice that there are no text objects for quotes or brackets: impossible to do something like da" (to delete a quoted substring) or ci( (to change inside parenthesis). Zsh supports these, you just need to generate and bind them to specific Zsh widgets:
autoload -Uz select-bracketed select-quoted
+zle -N select-quoted
+zle -N select-bracketed
+for km in viopp visual; do
+ bindkey -M $km -- '-' vi-up-line-or-history
+ for c in {a,i}${(s..)^:-\'\"\`\|,./:;=+@}; do
+ bindkey -M $km $c select-quoted
+ done
+ for c in {a,i}${(s..)^:-'()[]{}<>bB'}; do
+ bindkey -M $km $c select-bracketed
+ done
+done
+If you want to know more about Zsh widgets, I’ve written another article about that, where I also explain the code above.
Surrounding
Zsh also allows us to mimic the famous Tim Pope’s surround plugin. Just add the following to your zshrc:
autoload -Uz surround
+zle -N delete-surround surround
+zle -N add-surround surround
+zle -N change-surround surround
+bindkey -M vicmd cs change-surround
+bindkey -M vicmd ds delete-surround
+bindkey -M vicmd ys add-surround
+bindkey -M visual S add-surround
+You can then use cs (change surrounding), ds (delete surrounding), ys (add surrounding) in Zsh’s NORMAL mode.
Zsh Plugins
The term “plugin”, as I use it, has nothing official. People often speak about Zsh plugins as external pieces of configuration you can add to your own.
There are many of these plugins available for Zsh. Many of them are part of Zsh frameworks.
Zsh Completions
By default, Zsh can complete already many popular CLIs like cd, cp, git, and so on.
The plugin zsh-completions add even more completions. The list of the newly supported CLIs is here
If you don’t use any of the program listed, you don’t need this plugin.
I added zsh-completion as a git submodule in my dotfiles. Then, you can automatically add every completion to your fpath, in your zshrc:
fpath=(/path/to/my/zsh/plugins/zsh-completions/src $fpath)
+You don’t need to load every completion file, one by one. If you look at the beginning of one of these files, you’ll see compdef. It’s a function from Zsh which load automagically the completion when it’s needed. The completion file itself only needs to be included in your fpath.
You can also cherry-pick the specific completions you want.
Zsh Syntax Highlighting
What about syntax highlighting in Zsh? That’s what zsh-syntax-highlighting is about.
You can source it directly:
source /path/to/my/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
+There is one minor downside however: it seems to be currently incompatible with the surround widget we’ve seen above. If you want to use both, you need to use the branch feature/redrawhook.
You should source this plugin at the bottom of your zshrc. Everything loaded before will then be able to use syntax highlighting if needed.
Jumping To A Parent Directory Easily
Do you like to type cd ../../.. to come back to the great-grand-parent of the current folder?
Me neither.
It’s where bd can help you. Imagine that you’re in the folder ~/a/b/c/d. You can jump directly to a with the command bd a.
The Zsh completion is even included. Awesomeness!
To use it, you need to source the file bd.zsh.
Custom Scripts
Using a shell allows you to automate many parts of your workflow with shell scripts. That’s a huge benefit you should take advantage of.
I keep most of my scripts in one file and I document them (roughly) for me to remember what’s in there, and for others to get inspired.
I source the functions in my .zshrc, but you could autoload them too.
While working, ask yourself what tasks you do again and again, to automate them as much as you can. This is the real power of the shell, and it will make your whole workflow more fun.
External Programs
A shell without CLIs would be useless. Here are my personal favorites to expand Zsh functionalities.
Multiplex Your Zsh With tmux
I’ve already written about tmux here. It’s a terminal multiplexer with a tonne of functionalities: you can split your terminal in many windows or panes, synchronize them, and keep your sessions alive even without terminal. You can even extend it with plugins helping you automating your whole shell workflow.
Fuzzy Search With fzf
The fuzzy finder fzf is a fast and powerful tool. You can use it to search anything you want, like a file, an entry in your command line history, or a specific git commit message.
I wrote (or copied and pasted) a bunch of scripts using zsh too, to search through git logs or tmuxp projects.
There are different ways to install fzf. You’ll need first the executable. Then, I would recommend sourcing the files:
key-bindings.zsh, which will include some practical keystrokes likeCtrl-horCtrl-tcompletion.zsh, forfzfcompletion.
If you use Arch Linux, you’ll need to install the package fzf and simply source these two files in your zshrc:
source /usr/share/fzf/completion.zsh
+source /usr/share/fzf/key-bindings.zsh
+Otherwise, you’ll need to follow the installation process from fzf’s README file.
The Z-Shell Is Now Yours
You should now have a clean and lean Zsh configuration, and you should understand enough of it to customize it.
What did we learn with this article?
- Zsh reads its configuration files in a precise order.
- You can set (or unset) many Zsh options depending on your needs.
- The completion system of Zsh is one of its best feature.
- Zsh directory stack allow you to jump easily in directories you’ve already visited.
- If you like Vim, Zsh allows you to use keystrokes from the Vim world. You can even edit your commands directly in Vim.
- External plugins can be found on The Internet, to improve even further the Zsh experience.
- You should go crazy on shell scripting, to automate your workflow as much as you can.
- External programs can enhance your experience with the shell, like
tmuxorfzf.
All your colleagues will be jealous. Guaranteed.
+
+
+
+
+
+
+
+
+
+
+```
+
+Here is an example that shows an image of two (2) dogs:
+
+
+
+Image of two dogs
+
+And here's an example of an image that illustrates the use of alt text:
+
+
+
+Image of dog with alt text displayed
+
+==You should also describe your icon buttons.==
+
+Icons can be easily understood most of the time. It's widely recognized that an x symbol, like this ❌, typically closes a window, a check mark ✅ signifies completion, a forward arrow ▶ signifies send (or play), and a plus sign ➕ represents addition.
+
+But this is clear only for individuals with visual capabilities. For people who aren't able to see the buttons, you'll need to provide a description so they know what that button does.
+
+Let's take a look at this HTML and CSS code that shows how to make buttons access:
+
+Document
+
+Here's the result of the code implemented above:
+
+
+
+### Is it Operable?
+
+Users should be able to navigate and interact with the interface quickly. Consider the following factors:
+
+==First, make sure you use clear and consistent headings.==
+
+This is what clear and consistent headings look like:
+
+## I am a Title
+
+## I am a Subtitle
+
+### This is heading 3
+
+#### This is Heading 4
+
+##### This is Heading 5
+
+###### This is heading 6
+
+As you can see, these headings go from largest to smallest in order. We have an H1 heading first, followed by H2, H3, and so on.
+
+Here are some headings that don't follow the proper hierarchy:
+
+###### This is heading 6
+
+##### This is Heading 5
+
+#### This is Heading 4
+
+### This is heading 3
+
+## I am a Subtitle
+
+## I am a Title
+
+In this example, the headings go in reverse order, starting from H6 and moving up through H5, H4, and so on.
+
+Just remember to use proper heading hierarchy – don't use an H2 and then jump straight to H4 for a subheading, for example, as this is visually jarring and doesn't convey the proper importance or hierarchy of the text.
+
+Here's why heading hierarchy is important:
+
+* A clear heading hierarchy helps readers easily navigate and understand the content of a document.
+* Heading hierarchy is crucial for accessibility, as it helps screen readers and assistive technologies interpret the structure of the content. This is important for individuals with visual impairments who rely on such tools to access information.
+* A well-organized heading hierarchy implement a logical flow of information, ensuring that topics are presented in a coherent order.
+
+Also, refrain from using elements that might trigger physical discomfort, like bright flashing lights.
+
+==And make sure you think about== ==[keyboard accessibility](https://www.freecodecamp.org/news/designing-keyboard-accessibility-for-complex-react-experiences/)== ==so users can navigate and communicate using the keyboard, and not exclusively using a mouse.==
+
+### Is it Understandable?
+
+Content and functionality should be presented clearly and understandably. Consider the following factors:
+
+* ==Organize content using headings, subheadings, and bullet points to enhance readability.==
+* Provide instructions and error messages that are easy to understand.
+* Use simple and concise language, avoid complex terms.
+
+### Is it Robust?
+
+Websites should be built using robust and widely supported technologies to enable compatibility across devices and assistive technologies.
+
+You'll want to maximize compatibility with current and future user agents, including assistive technologies.
+
+Here are some of the ways you can maximize compatibility with current and future agents, including assistive tools:
+
+* ==Use== ==[HTML5 semantic elements](https://www.freecodecamp.org/news/semantic-html-alternatives-to-using-divs/)== ==like== `==<====header====>==`==,== `==<====nav====>==`==,== `==<====main====>==`==, and== `==<====footer====>==` ==to enhance the document's structure.==
+* ==Ensure that your== ==[JavaScript code is efficient](https://www.freecodecamp.org/news/javascript-performance-async-defer/)== ==and doesn't block the rendering process.==
+* ==Utilize== ==[browser developer tools](https://www.freecodecamp.org/news/learn-how-to-use-the-chrome-devtools-to-troubleshoot-websites/)== ==and online testing services to identify and fix compatibility issues.==
+* ==Conduct== ==[usability testing](https://www.freecodecamp.org/news/10-best-ux-testing-software-tools/)== ==with a diverse group of users, including those who rely on assistive technologies, to gather feedback and make improvements.==
+* ==Optimize your website for fast loading times and low data usage using techniques like== ==[caching](https://www.freecodecamp.org/news/a-detailed-guide-to-pre-caching/)== ==and== ==[tools like CDNs](https://www.freecodecamp.org/news/cdns-speed-up-performance-by-reducing-latency/)== ==to reduce latency. This benefits both accessibility and user experience.==
+* ==Document your code and accessibility features for future maintainers.==
+* ==Test== ==[website compatibility across various browsers](https://www.freecodecamp.org/news/cross-browser-compatibility-testing-best-practices-for-web-developers/)====. Testing website compatibility involves ensuring that your website functions correctly and looks good on a variety of devices, browsers, and assistive technologies.==
+
+Here are the steps you can follow to test website compatibility effectively:
+
+1. **Device Testing**: Test your website on various devices, such as desktop computers, laptops, tablets, and smartphones. This includes both iOS and Android devices.
+2. **Browser Testing**: Check your website's performance and appearance on multiple browsers, including but not limited to Google Chrome, Mozilla Firefox, Apple Safari, and Microsoft Edge.
+3. **User Testing**: Conduct usability testing with real users. Ask them to use your website on different devices and browsers and collect feedback on compatibility issues.
+4. **Performance Testing**: Assess website loading times, and optimize for speed using tools like Google PageSpeed Insights, GTmetrix, or Lighthouse. Check for compatibility with slow internet connections.
+
+## Conclusion
+
+Understanding web accessibility can enhance the user experience by creating a smooth and seamless interaction with websites and web applications.
+
+Implementing these tips can improve the overall user-friendliness and navigability of your app. It'll help create a more enjoyable experience for all users, and will also allow people with disabilities to perceive, understand, navigate, and interact with your sites effectively.
+
+---
+
+---
+
+ Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. [Get started](https://www.freecodecamp.org/learn/)
\ No newline at end of file
diff --git a/Omnivore/2023-11-04 - Git Merge vs Rebase vs Squash ¿Qué estrategia debemos elegir-.md b/Omnivore/2023-11-04 - Git Merge vs Rebase vs Squash ¿Qué estrategia debemos elegir-.md
new file mode 100644
index 0000000..d8de707
--- /dev/null
+++ b/Omnivore/2023-11-04 - Git Merge vs Rebase vs Squash ¿Qué estrategia debemos elegir-.md
@@ -0,0 +1,27 @@
+---
+id: fc51bf82-66d3-451f-8f64-17d6add50f92
+title: |
+ Git Merge vs Rebase vs Squash ¿Qué estrategia debemos elegir?
+status: ARCHIVED
+tags:
+ - read-later
+ - Youtube
+date_added: 2023-11-04 14:14:49
+url_omnivore: |
+ https://omnivore.app/me/https-www-youtube-com-watch-pp-yg-ukz-2-l-0-ih-nxd-w-fza-a-253-d-18b9b548407
+url_original: |
+ https://www.youtube.com/watch?pp=ygUKZ2l0IHNxdWFzaA%253D%253D&v=HlmZLXMOpEM
+---
+
+# Git Merge vs Rebase vs Squash ¿Qué estrategia debemos elegir?
+
+## Notes
+
+- Merge commit: Se crea un commit que tiene 2 padres, el último commit de main y la feature branch, se mantiene la trazabilidad hacia la feature branch pero el historial queda visualmente más complejo
+- Rebase: Se copian los commits de la feature branch a main como nuevos commits, se pierde la trazabilidad hacia la feature branch pero queda un historial lineal en main
+- Squash commit: Se juntan todos los commits en uno solo con un squash que queda en main, se pierde la trazabilidad hacia la feature branch pero queda un historial lineal en main
+## Original
+
+[Git Merge vs Rebase vs Squash ¿Qué estrategia debemos elegir?](https://www.youtube.com/watch?pp=ygUKZ2l0IHNxdWFzaA%253D%253D&v=HlmZLXMOpEM)
+
+By [CodelyTV - Redescubre la programación](https://www.youtube.com/@CodelyTV)
\ No newline at end of file
diff --git a/Omnivore/2023-11-06 - How to Write Components that Work in Any Framework.md b/Omnivore/2023-11-06 - How to Write Components that Work in Any Framework.md
new file mode 100644
index 0000000..628e953
--- /dev/null
+++ b/Omnivore/2023-11-06 - How to Write Components that Work in Any Framework.md
@@ -0,0 +1,402 @@
+---
+id: 616d5d08-7d04-11ee-8eaa-9f56108b78ec
+title: |
+ How to Write Components that Work in Any Framework
+status: ARCHIVED
+tags:
+ - read-later
+ - RSS
+date_added: 2023-11-06 17:25:12
+url_omnivore: |
+ https://omnivore.app/me/how-to-write-components-that-work-in-any-framework-18ba72d0079
+url_original: |
+ https://www.freecodecamp.org/news/write-components-that-work-in-any-framework/
+---
+
+# How to Write Components that Work in Any Framework
+
+## Highlights
+
+With Custom Elements you can author your own custom HTML elements that you can reuse across your site. They can be as simple as text, images, or visual decorations. You can push them further and build interactive components, complex widgets, or entire web applications.
+
+[source](https://omnivore.app/me/how-to-write-components-that-work-in-any-framework-18ba72d0079#bceef8c0-728e-422a-aed6-b047736cb395)
+
+---
+
+### Writing a web component requires understanding all of its underlying technologies
+
+As we saw above, web components are made up of three technologies. You can also see in the hello world code snippet, that we explicitly need to know and understand these three technologies.
+
+1. We’re creating a **template element** and setting its inner HTML
+2. We’re creating a **shadow root**, and explicitly setting its mode to ‘open’.
+3. We’re cloning our **template** and appending it to our **shadow root**
+4. We’re registering a new **custom element** to the document
+
+[source](https://omnivore.app/me/how-to-write-components-that-work-in-any-framework-18ba72d0079#46fc130a-1549-40c8-b950-42035c227bc4)
+
+---
+
+As web component authors, we need to consider a lot of things:
+
+* Setting up the shadow DOM
+* Setting up the HTML templates
+* Cleaning up event listeners
+* Defining properties that we want to observe
+* Reacting to properties when they change
+* Handling type conversions for attributes
+
+[source](https://omnivore.app/me/how-to-write-components-that-work-in-any-framework-18ba72d0079#855f444c-49f1-4176-9537-aaeeb6a01355)
+
+---
+
+One such tool is called Lit, which is developed by a team at Google. [Lit](https://lit.dev/) is a lightweight library designed to make writing web components simple, by removing the need for the boilerplate we’ve already seen above.
+
+[source](https://omnivore.app/me/how-to-write-components-that-work-in-any-framework-18ba72d0079#385d9ef8-13fb-4799-bff5-ef767b3df67f)
+
+---
+
+## Original
+
+
+
+The browser has a built-in way of writing reusable components in the form of **web components**. They’re an excellent choice for building interactive and reusable components that work in any frontend framework.
+
+With that said, writing highly interactive and robust web components isn’t simple. They require a lot of boilerplate and feel much less intuitive than the components you may have written in frameworks like React, Svelte, or Vue.
+
+In this tutorial, I’ll give you an example of an interactive component written as a web component, and then refactor it using a library that softens the edges and removes heaps of boilerplate.
+
+Don’t sweat it if you’re not familiar with web components. In the next section, I’ll do a (brief) overview of what web components are, and what they’re made out of. If you have some basic experience with them, you can skip the next section.
+
+## What are Web Components?
+
+Before web components, the browser didn’t have a standard way of writing reusable components. Many libraries solve this problem, but they often run into limitations like performance, interoperability, and issues with web standards.
+
+Web components are a technology made up of 3 different browser features:
+
+* Custom elements
+* Shadow DOM
+* HTML Templates
+
+We’ll do a quick crash course covering these technologies, but it’s by no means a comprehensive breakdown.
+
+### What are Custom Elements?
+
+==With Custom Elements you can author your own custom HTML elements that you can reuse across your site. They can be as simple as text, images, or visual decorations. You can push them further and build interactive components, complex widgets, or entire web applications.==
+
+You’re not just limited to using them in your projects, but you can publish them and allow other developers to use them on their sites.
+
+Here are some of the reusable components from my library [A2K](https://a2000-docs.netlify.app/). You can see that they come in all shapes and sizes, and have a range of different functionalities. Using them in your projects is similar to using any old HTML element.
+
+
+
+A small collection of web components from the A2K library
+
+Here’s how you’d use the progress element in your project:
+
+```xml
+
+
+
+ 