Rename language articles

This commit is contained in:
Angelos Chalaris
2023-05-18 23:57:15 +03:00
parent 45d1fa023a
commit 2d58c1dfd7
48 changed files with 17 additions and 17 deletions

View File

@ -1,54 +0,0 @@
---
title: 10 must-have VS Code extensions for JavaScript developers
shortTitle: Essential VS Code extensions
type: story
language: javascript
tags: [devtools,vscode]
author: chalarangelo
cover: computer-screens
excerpt: Boost your productivity with these 10 essential VS Code extensions for JavaScript developers.
unlisted: true
dateModified: 2021-06-12T19:30:41+03:00
---
Developers will most likely argue for the rest of eternity about the most productive code editor and the best extensions. Here are my personal extension preferences for VS Code as a JavaScript developer:
### ESLint
[ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) turns the popular JavaScript linter into an extension of VS Code. It automatically reads your linting configuration, identifies problems and even fixes them for you, if you want.
### GitLens
[GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) is a very powerful collaboration tool for VS Code. It provides many useful tools for git such as blame, code authorship, activity heatmaps, recent changes, file history and even commit search.
### Debugger for Chrome
[Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) allows you to debug your JavaScript code in Chrome or Chromium. Breakpoints, call stack inspection and stepping inside a function are only some of its features.
### Bracket Pair Colorizer 2
[Bracket Pair Colorizer 2](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2) makes reading code faster as it makes matching brackets the same color. This extension for VS Code improves upon its predecessor by providing improved performance.
### Bookmarks
[Bookmarks](https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks) is one of those extensions that will significantly reduce your time jumping between different files, as it allows you to save important positions and navigate back to them easily and quickly.
### TODO Highlight
[TODO Highlight](https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight) simplifies tracking leftover tasks by allowing you to list all of your TODO annotations, as well as adding a handy background highlight to them to make them pop out immediately.
### Live Server
[Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) gives you an easy way to serve web pages from VS Code, making previewing and debugging a lot easier. One of the core features is the live reload support that many developers are used to.
### REST Client
[REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) allows you to send HTTP requests and view the responses directly in VS Code. This extension supports a wide range of formats and authorization and should work with most setups.
### One Dark Pro
[One Dark Pro](https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme) is one of the most popular VS Code themes and with very good reason. It provides a clean theme with a nice palette that has great contrast and is very comfortable to use on a daily basis.
### Fira Code
[Fira Code](https://github.com/tonsky/FiraCode) is not a traditional VS Code extension and might take a couple more steps to set up, but it's a superb programming font with ligatures that will help you scan code faster once you get used to it.

View File

@ -1,303 +0,0 @@
---
title: 25 CSS gradients for your next project
shortTitle: CSS gradients
type: cheatsheet
language: css
tags: [visual]
author: chalarangelo
cover: colors-mural
excerpt: We hand picked 25 of our favorite CSS gradients from uiGradients for your next design. Get them now!
dateModified: 2021-06-12T19:30:41+03:00
---
[uiGradients](https://uigradients.com/) has an amazing collection of ready-to-use CSS gradients for pretty much anything. I highly recommend checking out the full collection. Meantime, here are our top picks in case you're looking for some color:
<style>
.gradient-box {
width: 100%;
height: 64px;
border-radius: 8px 8px 0 0;
margin-bottom: -16px;
margin-top: 32px;
}
.gradient-box + .gatsby-highlight > pre.blog-code {
border-radius: 0 0 8px 8px;
}
.stripe {
background: linear-gradient(to right, #1fa2ff, #12d8fa, #a6ffcb);
}
.flare {
background: linear-gradient(to right, #f12711, #f5af19);
}
.vanusa {
background: linear-gradient(to right, #da4453, #89216b);
}
.sublime-light {
background: linear-gradient(to right, #fc5c7d, #6a82fb);
}
.bighead {
background: linear-gradient(to right, #c94b4b, #4b134f);
}
.velvet-sun {
background: linear-gradient(to right, #e1eec3, #f05053);
}
.relay {
background: linear-gradient(to right, #3a1c71, #d76d77, #ffaf7b);
}
.crystal-clear {
background: linear-gradient(to right, #159957, #155799);
}
.celestial {
background: linear-gradient(to right, #c33764, #1d2671);
}
.ibiza-sunset {
background: linear-gradient(to right, #ee0979, #ff6a00);
}
.fresh-turboscent {
background: linear-gradient(to right, #f1f2b5, #135058);
}
.cheer-up-emo-kid {
background: linear-gradient(to right, #556270, #ff6b6b);
}
.starfall {
background: linear-gradient(to right, #f0c27b, #4b1248);
}
.nelson {
background: linear-gradient(to right, #f2709c, #ff9472);
}
.forever-lost {
background: linear-gradient(to right, #5d4157, #a8caba);
}
.blurry-beach {
background: linear-gradient(to right, #d53369, #cbad6d);
}
.influenza {
background: linear-gradient(to right, #c04848, #480048);
}
.calm-darya {
background: linear-gradient(to right, #5f2c82, #49a09d);
}
.titanium {
background: linear-gradient(to right, #283048, #859398);
}
.pinky {
background: linear-gradient(to right, #dd5e89, #f7bb97);
}
.purple-paradise {
background: linear-gradient(to right, #1d2b64, #f8cdda);
}
.horizon {
background: linear-gradient(to right, #003973, #e5e5be);
}
.noon-to-dusk {
background: linear-gradient(to right, #ff6e7f, #bfe9ff);
}
.jshine {
background: linear-gradient(to right, #12c2e9, #c471ed, #f64f59);
}
.argon {
background: linear-gradient(to right, #03001e, #7303c0, #ec38bc, #fdeff9);
}
</style>
<div class="gradient-box stripe"></div>
```css
.stripe {
background: linear-gradient(to right, #1fa2ff, #12d8fa, #a6ffcb);
}
```
<div class="gradient-box flare"></div>
```css
.flare {
background: linear-gradient(to right, #f12711, #f5af19);
}
```
<div class="gradient-box vanusa"></div>
```css
.vanusa {
background: linear-gradient(to right, #da4453, #89216b);
}
```
<div class="gradient-box sublime-light"></div>
```css
.sublime-light {
background: linear-gradient(to right, #fc5c7d, #6a82fb);
}
```
<div class="gradient-box bighead"></div>
```css
.bighead {
background: linear-gradient(to right, #c94b4b, #4b134f);
}
```
<div class="gradient-box velvet-sun"></div>
```css
.velvet-sun {
background: linear-gradient(to right, #e1eec3, #f05053);
}
```
<div class="gradient-box argon"></div>
```css
.argon {
background: linear-gradient(to right, #03001e, #7303c0, #ec38bc, #fdeff9);
}
```
<div class="gradient-box celestial"></div>
```css
.celestial {
background: linear-gradient(to right, #c33764, #1d2671);
}
```
<div class="gradient-box relay"></div>
```css
.relay {
background: linear-gradient(to right, #3a1c71, #d76d77, #ffaf7b);
}
```
<div class="gradient-box crystal-clear"></div>
```css
.crystal-clear {
background: linear-gradient(to right, #159957, #155799);
}
```
<div class="gradient-box ibiza-sunset"></div>
```css
.ibiza-sunset {
background: linear-gradient(to right, #ee0979, #ff6a00);
}
```
<div class="gradient-box fresh-turboscent"></div>
```css
.fresh-turboscent {
background: linear-gradient(to right, #f1f2b5, #135058);
}
```
<div class="gradient-box cheer-up-emo-kid"></div>
```css
.cheer-up-emo-kid {
background: linear-gradient(to right, #556270, #ff6b6b);
}
```
<div class="gradient-box starfall"></div>
```css
.starfall {
background: linear-gradient(to right, #f0c27b, #4b1248);
}
```
<div class="gradient-box nelson"></div>
```css
.nelson {
background: linear-gradient(to right, #f2709c, #ff9472);
}
```
<div class="gradient-box forever-lost"></div>
```css
.forever-lost {
background: linear-gradient(to right, #5d4157, #a8caba);
}
```
<div class="gradient-box blurry-beach"></div>
```css
.blurry-beach {
background: linear-gradient(to right, #d53369, #cbad6d);
}
```
<div class="gradient-box influenza"></div>
```css
.influenza {
background: linear-gradient(to right, #c04848, #480048);
}
```
<div class="gradient-box jshine"></div>
```css
.jshine {
background: linear-gradient(to right, #12c2e9, #c471ed, #f64f59);
}
```
<div class="gradient-box calm-darya"></div>
```css
.calm-darya {
background: linear-gradient(to right, #5f2c82, #49a09d);
}
```
<div class="gradient-box titanium"></div>
```css
.titanium {
background: linear-gradient(to right, #283048, #859398);
}
```
<div class="gradient-box pinky"></div>
```css
.pinky {
background: linear-gradient(to right, #dd5e89, #f7bb97);
}
```
<div class="gradient-box purple-paradise"></div>
```css
.purple-paradise {
background: linear-gradient(to right, #1d2b64, #f8cdda);
}
```
<div class="gradient-box horizon"></div>
```css
.horizon {
background: linear-gradient(to right, #003973, #e5e5be);
}
```
<div class="gradient-box noon-to-dusk"></div>
```css
.noon-to-dusk {
background: linear-gradient(to right, #ff6e7f, #bfe9ff);
}
```

View File

@ -1,59 +0,0 @@
---
title: 4 JavaScript Array methods you must know
shortTitle: Useful array methods
type: story
language: javascript
tags: [array,cheatsheet]
author: chalarangelo
cover: arrays
excerpt: JavaScript arrays have a very robust API offering some amazing tools. Learn the 4 must-know JavaScript array methods in this quick guide.
dateModified: 2021-06-12T19:30:41+03:00
---
JavaScript arrays have a very robust API offering a plethora of amazing tools. Here are our top 4 JavaScript array methods every developer should know:
### Array.prototype.map()
[`Array.prototype.map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) creates a new array by applying the provided transformation to each element of the original array. The result is an array with the same length as the original array and elements transformed based on the provided function.
```js
const arr = [1, 2, 3];
const double = x => x * 2;
arr.map(double); // [2, 4, 6]
```
### Array.prototype.filter()
[`Array.prototype.filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) creates a new array by using a filtering function to keep only elements that return `true` based on that function. The result is an array with equal or less than the original array's length, containing a subset of the same elements as the original array.
```js
const arr = [1, 2, 3];
const isOdd = x => x % 2 === 1;
arr.filter(isOdd); // [1, 3]
```
![JavaScript Array Methods](./illustrations/js-array-methods.png)
### Array.prototype.reduce()
[`Array.prototype.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) creates an output value of any type depending on a reducer function and an initial value. The result can be of any type such as an integer, an object or an array, based on the reducer function provided.
```js
const arr = [1, 2, 3];
const sum = (x, y) => x + y;
arr.reduce(sum, 0); // 6
const increment = (x, y) => [...x, x[x.length - 1] + y];
arr.reduce(increment, [0]); // [0, 1, 3, 6]
```
### Array.prototype.find()
[`Array.prototype.find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) returns the first element for which a matcher function returns `true`. The result is a single element from the original array.
```js
const arr = [1, 2, 3];
const isOdd = x => x % 2 === 1;
arr.find(isOdd); // 1
```

View File

@ -1,121 +0,0 @@
---
title: 6 JavaScript Regular Expression features you can use today
shortTitle: JavaScript Regular Expression tips
type: story
language: javascript
tags: [string,regexp]
author: chalarangelo
cover: taking-photos
excerpt: Regular expressions are very powerful, but hard to master. Understand these features and start using them in your JavaScript code.
dateModified: 2021-06-12T19:30:41+03:00
---
Regular expressions, while very powerful, are notoriously hard to master. Here are 6 useful features that can help you start using them in your JavaScript projects:
### Capturing groups
Capturing groups allow you to get specific parts of the matched string, simply by wrapping part of the regular expression in parentheses `(...)`:
```js
const str = 'JavaScript is a programming language';
/(JavaScript) is a (.*)/.exec(str);
/*
[
0: 'JavaScript is a programming language',
1: 'JavaScript',
2: 'programming language'
]
*/
```
### Non-capturing groups
Non-capturing groups are used for matching something without capturing it, like an either/or matching group that you do not really need. They are defined similarly to capturing groups, but prefixed with `?:`:
```js
const str = 'JavaScript is a programming language';
/(?:JavaScript|Python) is a (.+)/.exec(str);
/*
[
0: 'JavaScript is a programming language',
1: 'programming language'
]
*/
```
### Named capturing groups
Named capturing groups allow you to name a capturing group, by prefixing it with `<name>`:
```js
const str = 'JavaScript is a programming language';
/(?<subject>.+) is a (?<description>.+)/.exec(str);
/*
[
0: 'JavaScript is a programming language',
1: 'JavaScript',
2: 'programming language',
groups: {
subject: 'JavaScript,
description: 'programming language'
}
]
*/
```
### Capturing group backreferences
Backreferences help you write shorter regular expressions, by repeating an existing capturing group, using `\1`, `\2` etc. Similarly, you can also repeat named capturing groups using `\k<name>`:
```js
const str = 'JavaScript is a programming language - an awesome programming language JavaScript is';
/(.+) is a (?<description>.+) - an awesome \k<description> \1 is/.exec(str);
/*
[
0: 'JavaScript is a programming language - an awesome programming language JavaScript is',
1: 'JavaScript',
2: 'programming language',
groups: {
subject: 'JavaScript,
description: 'programming language'
}
]
*/
```
### Lookaheads
Lookaheads allow you to check if something is followed by a certain pattern, without actually matching it. You can create positive lookaheads using `?=` and negative lookaheads using `?!`:
```js
const str = 'JavaScript is not the same as Java and you should remember that';
/Java(?=Script)(.*)/.exec(str);
/*
[
0: 'JavaScript is not the same as Java and you should remember that',
1: 'Script is not the same as Java and you should remember that'
]
*/
/Java(?!Script)(.*)/.exec(str);
/*
[
0: 'Java and you should remember that',
1: ' and you should remember that'
]
*/
```
### Unicode characters
Finally, you can match unicode characters, using `/p{...}` and the `/u` flag. Examples include, but are not limited to `{Emoji}`, `{Math_Symbols}` and `{Script=Greek}`:
```js
const str = 'Greek looks like this: γεια';
/\p{Script=Greek}+/u.exec(str);
/*
[
0: 'γεια'
]
*/
```

View File

@ -1,80 +0,0 @@
---
title: 6 Python f-strings tips and tricks
type: story
language: python
tags: [string]
author: chalarangelo
cover: sea-view
excerpt: Python's f-strings can do a lot more than you might expect. Learn a few useful tips and tricks in this quick guide.
dateModified: 2021-07-20T05:00:00-04:00
---
Python's f-strings provide a more readable, concise and less error-prone way to format strings than traditional string formatting. They are packed with useful features that are sure to come in handy in day-to-day use. Let's take a look at some of them.
### String Interpolation
The most used f-string feature by far is string interpolation. All you need to do is wrap the value or variable in curly braces (`{}`) and you're good to go.
```py
str_val = 'apples'
num_val = 42
print(f'{num_val} {str_val}') # 42 apples
```
### Variable names
Apart from getting a variable's value, you can also get its name alongside the value. This can be especially useful when debugging and can be easily accomplished by adding an equals sign (`=`) after the variable name inside the curly braces.
Bear in mind that whitespace inside the curly braces is taken into account, so adding spaces around the equals sign can make for a more readable result.
```py
str_val = 'apples'
num_val = 42
print(f'{str_val=}, {num_val = }') # str_val='apples', num_val = 42
```
### Mathematical operations
Not syntactically unlike variable names, you can also perform mathematical operations in f-strings. You can place the mathematical expression inside the curly braces and, if you add an equal sign, you'll get the expression and its result.
```py
num_val = 42
print(f'{num_val % 2 = }') # num_val % 2 = 0
```
### Printable representation
Apart from plain string interpolation, you might want to get the printable representation of a value. This is already easy to accomplish using the `repr()` function. f-strings provide a much shorter syntax by appending a `!r` inside the curly braces.
```py
str_val = 'apples'
print(f'{str_val!r}') # 'apples'
```
### Number formatting
Additionally, f-strings can also be used for formatting - hence the **f** in the name. To add formatting to a value you can add a colon (`:`) followed by a format specifier. This can also be combined with the equals sing from before, shall you want to print the name of the variable as well.
Numbers are a great candidate for this. If, for example, you want to trim a numeric value to two digits after the decimal, you can use the `.2f` format specifier.
```py
price_val = 6.12658
print(f'{price_val:.2f}') # 6.13
```
### Date formatting
Finally, dates can also be formatted the same way as numbers, using format specifiers. As usual, `%Y` denotes the full year, `%m` is the month and `%d` is the day of the month.
```py
from datetime import datetime;
date_val = datetime.utcnow()
print(f'{date_val=:%Y-%m-%d}') # date_val=2021-07-09
```

View File

@ -1,113 +0,0 @@
---
title: Asynchronous JavaScript Cheat Sheet
type: cheatsheet
language: javascript
tags: [function,promise]
author: chalarangelo
cover: green-plant
excerpt: Learn everything you need to know about promises and asynchronous JavaScript with this handy cheatsheet.
dateModified: 2021-06-12T19:30:41+03:00
---
### Promise basics
- **Promises** start in a **pending state**, neither fulfilled or rejected.
- When the operation is completed, a promise will become **fulfilled with a value**.
- If the operation fails, a promise will get **rejected with an error**.
### Creating promises
- The function passed to the `Promise` constructor will execute synchronously.
- Use `resolve()` or `reject()` to create promises from values.
- `Promise.resolve(val)` will fulfill the promise with `val`.
- `Promise.reject(err)` will reject the promise with `err`.
- If you put a fulfilled promise into a fulfilled promise, they will collapse into one.
```js
// Resolving with a value, rejecting with an error
new Promise((resolve, reject) => {
performOperation((err, val) => {
if (err) reject(err);
else resolve(val);
});
});
// Resolving without value, no need for reject
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
```
### Handling promises
- `Promise.prototype.then()` accepts two optional arguments (`onFulfilled`, `onRejected`).
- `Promise.prototype.then()` will call `onFulfilled` once the promise is fulfilled.
- `Promise.prototype.then()` will call `onRejected` if the promise is rejected.
- `Promise.prototype.then()` passes errors through if `onRejected` in undefined.
- `Promise.prototype.catch()` accepts one argument (`onRejected`).
- `Promise.prototype.catch()` behaves like `Promise.prototype.then()` when `onFulfilled` is omitted.
- `Promise.prototype.catch()` passes fulfilled values through.
- `Promise.prototype.finally()` accepts one argument (`onFinally`).
- `Promise.prototype.finally()` calls `onFinally` with no arguments once any outcome is available.
- `Promise.prototype.finally()` passes through input promise.
```js
promisedOperation()
.then(
val => value + 1, // Called once the promise is fulfilled
err => { // Called if the promise is rejected
if (err === someKnownErr) return defaultVal;
else throw err;
}
)
.catch(
err => console.log(err); // Called if the promise is rejected
)
.finally(
() => console.log('Done'); // Called once any outcome is available
);
```
- All three of the above methods will not be executed at least until the next tick, even for promises that already have an outcome.
### Combining promises
- `Promise.all()` turns an array of promises into a promise of an array.
- If any promise is rejected, the error will pass through.
- `Promise.race()` passes through the first settled promise.
```js
Promise
.all([ p1, p2, p3 ])
.then(([ v1, v2, v3 ]) => {
// Values always correspond to the order of promises,
// not the order they resolved in (i.e. v1 corresponds to p1)
});
Promise
.race([ p1, p2, p3 ])
.then(val => {
// val will take the value of the first resolved promise
});
```
### async/await
- Calling an `async` function always results in a promise.
- `(async () => value)()` will resolve to `value`.
- `(async () => throw err)()` will reject with an error.
- `await` waits for a promise to be fulfilled and returns its value.
- `await` can only be used in `async` functions.
- `await` also accepts non-promise values.
- `await` always waits at least until the next tick before resolving, even when waiting already fulfilled promises or non-promise values.
```js
async () => {
try {
let val = await promisedValue();
// Do stuff here
} catch (err) {
// Handle error
}
}
```

View File

@ -1,64 +0,0 @@
---
title: Big-O Cheat Sheet
type: cheatsheet
language: javascript
tags: [algorithm]
author: chalarangelo
cover: light-ring
excerpt: Learn everything you need to know about Big-O notation with this handy cheatsheet.
dateModified: 2023-01-08T05:00:00-04:00
---
### Definition
Big-O notation, represents an algorithm's **worst-case complexity**. It uses algebraic terms to describe the complexity of an algorithm, allowing you to measure its efficiency and performance. Below you can find a chart that illustrates Big-O complexity:
![Big-O Complexity Chart](./illustrations/big-o-complexity.png)
Simply put, `O(1)` stands for **constant time complexity**, which is the most efficient, while `O(n!)` stands for **factorial time complexity**, which is the least efficient. The `n` in the complexity represents the size of the input, so `O(n)` means that the algorithm's time complexity will grow linearly with the size of the input.
Apart from Big-O notation, there are other notations that are used to describe the complexity of an algorithm, such as `Ω` (Omega) and `Θ` (Theta). `Ω` describes the **best-case complexity** of an algorithm, while `Θ` describes the **average-case complexity** of an algorithm.
### Common Data Structure operations
Different data structures have different time complexities for the same operations. For example, a linked list has `O(1)` time complexity for `insert` and `delete` operations, while an array has `O(n)` time complexity for the same operations. Below you can find average and worst time complexities for data structures used commonly in web development.
#### Average time complexity
| Data Structure | Access | Search | Insertion | Deletion |
| --- | --- | --- | --- | --- |
| [**Array**](/js/s/native-data-structures) | Θ(1) | Θ(n) | Θ(n) | Θ(n) |
| [**Queue**](/js/s/data-structures-queue) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| [**Stack**](/js/s/data-structures-stack) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| [**Linked List**](/js/s/data-structures-linked-list) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| [**Doubly Linked List**](/js/s/data-structures-doubly-linked-list) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| **Skip List** | Θ(log n) | Θ(log n) | Θ(log n) | Θ(log n) |
| **Hash Table** | N/A | Θ(1) | Θ(1) | Θ(1) |
| [**Binary Search Tree**](/js/s/data-structures-binary-search-tree) | Θ(log n) | Θ(log n) | Θ(log n) | Θ(log n) |
#### Worst time complexity
| Data Structure | Access | Search | Insertion | Deletion |
| --- | --- | --- | --- | --- |
| [**Array**](/js/s/native-data-structures) | O(1) | O(n) | O(n) | O(n) |
| [**Queue**](/js/s/data-structures-queue) | O(n) | O(n) | O(1) | O(1) |
| [**Stack**](/js/s/data-structures-stack) | O(n) | O(n) | O(1) | O(1) |
| [**Linked List**](/js/s/data-structures-linked-list) | O(n) | O(n) | O(1) | O(1) |
| [**Doubly Linked List**](/js/s/data-structures-doubly-linked-list) | O(n) | O(n) | O(1) | O(1) |
| **Skip List** | O(n) | O(n) | O(n) | O(n) |
| **Hash Table** | N/A | O(n) | O(n) | O(n) |
| [**Binary Search Tree**](/js/s/data-structures-binary-search-tree) | O(n) | O(n) | O(n) | O(n) |
### Array sorting algorithms
Similar to data structures, different array sorting algorithms have different time complexities. Below you can find the best, average and worst time complexities for the most common array sorting algorithms.
| Algorithm | Best | Average | Worst |
| --- | --- | --- | --- |
| [**Quick sort**](/js/s/quick-sort) | Ω(n log n) | Θ(n log n) | O(n^2) |
| [**Merge sort**](/js/s/merge-sort) | Ω(n log n) | Θ(n log n) | O(n log n) |
| [**Heap sort**](/js/s/heapsort) | Ω(n log n) | Θ(n log n) | O(n log n) |
| [**Bubble sort**](/js/s/bubble-sort) | Ω(n) | Θ(n^2) | O(n^2) |
| [**Insertion sort**](/js/s/insertion-sort) | Ω(n) | Θ(n^2) | O(n^2) |
| [**Selection sort**](/js/s/selection-sort) | Ω(n^2) | Θ(n^2) | O(n^2) |
| [**Bucket sort**](/js/s/bucket-sort) | Ω(n+k) | Θ(n+k) | O(n^2) |

View File

@ -1,51 +0,0 @@
---
title: Breaking React - a common pattern to avoid
type: story
language: react
tags: [debugging]
author: chalarangelo
cover: broken-screen
excerpt: As powerful as React is, it is also quite fragile at places. Did you know that a few lines can easily break your entire React application?
dateModified: 2021-11-06T20:51:47+03:00
---
I am by no means an expert React engineer, but I have a couple years of experience under my belt. React is a powerful library for building user interfaces, but it's also quite fragile at places. A common bug I have encountered is caused by **direct DOM manipulation in combination with React**. This is sort of an anti-pattern, as it can break your entire React application under the right circumstances and it's hard to debug.
Here's [a minimal example](https://codepen.io/chalarangelo/pen/jOEojVJ?editors=0010) of how to reproduce this bug, before we dive into explaining the problem and how to fix it:
```jsx
const destroyElement = () =>
document.getElementById('app').removeChild(document.getElementById('my-div'));
const App = () => {
const [elementShown, updateElement] = React.useState(true);
return (
<div id='app'>
<button onClick={() => destroyElement()}>
Delete element via querySelector
</button>
<button onClick={() => updateElement(!elementShown)}>
Update element and state
</button>
{ elementShown ? <div id="my-div">I am the element</div> : null }
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')).render(
<App />
);
```
This is a pretty simple React application, with a container, two buttons and a state variable. The problem is it will crash if you click the button that calls `destroyElement()` and then click the other one. _Why?_ you might ask. The issue here might not be immediately obvious, but if you look at your browser console you will notice the following exception:
```
Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
```
This might still be cryptic, so let me explain what's going on. React uses its own representation of the DOM, called a **virtual DOM**, in order to figure out what to render. Usually, the virtual DOM will match the current DOM structure and React will process changes in props and state. It will then update the virtual DOM and then batch and send the necessary changes to the real DOM.
However, in this case React's virtual DOM and the real DOM are different, because of `destroyElement()` removing the `#my-div` element. As a result, when React tries to update the real DOM with the changes from the virtual DOM, the element cannot be removed as it doesn't exist anymore. This results in the above exception being thrown and your application breaking.
You can refactor `destroyElement()` to be part of the `App` component and interact with its state to fix the issue in this example. Regardless of the simplicity of the problem or the fix, it showcases how fragile React can be under circumstances. This is only compounded in a large codebase where many developers contribute code daily in different areas. In such a setting, issues like this can be easily introduced and tracking them down can be rather tricky. This is why I would advice you to be very careful when directly manipulating the DOM in combination with React.

View File

@ -1,77 +0,0 @@
---
title: Code Anatomy - For loops, array reduce and method chaining
shortTitle: For loops, array reduce and method chaining
type: story
language: javascript
tags: [array,iterator]
author: chalarangelo
cover: case-study
excerpt: There are many ways to iterate and transform array data in JavaScript. Learn how each one works and where you should use them.
dateModified: 2021-06-12T19:30:41+03:00
---
### For loops
```js
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
let filePaths = [];
for (let file of files) {
const fileName = file.trim();
if(fileName) {
const filePath = `~/cool_app/${fileName}`;
filePaths.push(filePath);
}
}
// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo']
```
- Any `for` loop can be used - [read more about the different JavaScript loops](/blog/s/javascript-for-in-for-of-foreach/).
- Less common nowadays, due to functional programming being more popular.
- Control over the iteration, such as skipping over elements or early `return`s.
- Resulting array needs to be declared beforehand, outside the loop.
- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements.
- `O(N)` complexity, each element will be iterated over only once.
### Array reduce
```js
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
const filePaths = files.reduce((acc, file) => {
const fileName = file.trim();
if(fileName) {
const filePath = `~/cool_app/${fileName}`;
acc.push(filePath);
}
return acc;
}, []);
// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo']
```
- Uses `Array.prototype.reduce()` with an empty array as the initial value.
- More common nowadays, due to functional programming being more popular.
- Less control over the iteration, cannot skip elements or `return` early.
- Can be chained with other methods, if necessary.
- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements.
- `O(N)` complexity, each element will be iterated over only once.
### Method chaining
```js
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
const filePaths = files
.map(file => file.trim())
.filter(Boolean)
.map(fileName => `~/cool_app/${fileName}`);
// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo']
```
- Uses `Array.prototype.map()` and `Array.prototype.filter()`.
- More common nowadays, due to functional programming being more popular.
- Less control over the iteration, cannot skip elements or `return` early.
- Declarative, easier to read and refactor, chain can grow as necessary.
- Does not use `Array.prototype.push()` or the spread (`...`) operator.
- `O(cN)` complexity, `c` iterations per element, (`c`: length of the chain).

View File

@ -1,122 +0,0 @@
---
title: Code Anatomy - Optimizing recursive functions
shortTitle: Optimizing recursive functions
type: story
language: javascript
tags: [recursion,performance]
author: chalarangelo
cover: case-study
excerpt: Recursive code tends to be inefficient or in need of optimization. Learn a couple of tricks we use to speed up our recursive functions.
dateModified: 2021-06-12T19:30:41+03:00
---
### Recursive functions
Recursion is a programming technique where the final solution is computed by breaking down the problem into smaller instances of the same problem and computing the solution for each one. The most common implementation is a function that calls itself, reducing the problem every time until it reaches an instance of the problem whose solution is either trivial to compute or already known. Let's look at a very well-known example, calculating the `n`th term of the [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number), implemented using recursion in JavaScript:
```js
const fibonacciNumber = n =>
n < 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n;
```
To understand recursion better, let's add a `console.log()` call before each `return` and figure out what exactly is happening:
```js
const fibonacciNumber = n => {
console.log(`[CALLED] fibonacciNumber(${n})`);
const r = n >= 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n;
console.log(`[RETURN] ${r} for n=${n}`);
return r;
}
fibonacciNumber(4);
// [CALLED] fibonacciNumber(4)
// [CALLED] fibonacciNumber(3)
// [CALLED] fibonacciNumber(2)
// [CALLED] fibonacciNumber(1)
// [RETURN] 1 for n=1
// [CALLED] fibonacciNumber(0)
// [RETURN] 0 for n=0
// [RETURN] 1 for n=2
// [CALLED] fibonacciNumber(1)
// [RETURN] 1 for n=1
// [RETURN] 2 for n=3
// [CALLED] fibonacciNumber(2)
// [CALLED] fibonacciNumber(1)
// [RETURN] 1 for n=1
// [CALLED] fibonacciNumber(0)
// [RETURN] 0 for n=0
// [RETURN] 1 for n=2
// [RETURN] 3 for n=4
```
As you can see, for each value of `n`, `fibonacciNumber` will be called twice, once with `n - 1` and once with `n - 2` and this will continue until it's called with either `1` or `0`. While this is straightforward to write and understand, it is inefficient as it will have to calculate the same value more than once.
### Calculation memoization
The solution to this problem, and the first trick that you can use to speed up recursive functions, is to use memoization. We already published [a great blog post on memoization](/js/s/memoization/) a little while back, so be sure to check it out to learn more about the subject. Here's our `fibonacciNumber` function, using memoization:
```js
const fibonacciCache = new Map();
const fibonacciNumber = n => {
console.log(`[CALL] fibonacciNumber(${n})`);
const cacheKey = `${n}`;
let r;
if(fibonacciCache.has(cacheKey)) {
r = fibonacciCache.get(cacheKey);
console.log(`[MEMO] Cache hit for ${n}: ${r}`);
}
else {
r = n >= 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n;
fibonacciCache.set(cacheKey, r);
console.log(`[CALC] Computed and stored value for ${n}: ${r}`);
}
return r;
}
fibonacciNumber(4);
// [CALL] fibonacciNumber(4)
// [CALL] fibonacciNumber(3)
// [CALL] fibonacciNumber(2)
// [CALL] fibonacciNumber(1)
// [CALC] Computed and stored value for 1: 1
// [CALL] fibonacciNumber(0)
// [CALC] Computed and stored value for 0: 0
// [CALC] Computed and stored value for 2: 1
// [CALL] fibonacciNumber(1)
// [MEMO] Cache hit for 1: 1
// [CALC] Computed and stored value for 3: 2
// [CALL] fibonacciNumber(2)
// [MEMO] Cache hit for 2: 1
// [CALC] Computed and stored value for 4: 3
```
As you can see in the example above, the value for each `n` is only computed once. While the Fibonacci sequence doesn't require any costly calculations, this could make a huge difference for a more computationally expensive problem. It will also be a lot more noticeable for higher values of `n` where the number of calculations will increase significantly.
### Using iteration
The second and final trick stems from the very definition of recursive programming turned on its head. If we can solve a smaller instance of the problem and use it for the solution of a larger instance of the problem, it should be possible to work iteratively from the smaller problem to the larger one, instead of recursively. Here's this idea in practice for our `fibonacciNumber` function:
```js
const fibonacciNumber = n => {
let r = 0, l = 1, s = 0;
for(let i = 0; i < n; i++) {
r = l;
l = s;
s = r + l;
console.log(`[CALC] i = ${i}: r = ${r}, l = ${l}, s = ${s}`);
}
return s;
}
fibonacciNumber(4);
// [CALC] i = 0: r = 1, l = 0, s = 1
// [CALC] i = 1: r = 0, l = 1, s = 1
// [CALC] i = 2: r = 1, l = 1, s = 2
// [CALC] i = 3: r = 1, l = 2, s = 3
```
The iterative solution above makes the same calculations as the memoized one, however it performs better due to two key reasons. First of all, there is no cache, which would take up space in memory, making the latter implementation require fewer resources. Similarly, as there are no recursive calls or checks for cache hits, the code performs better and requires fewer resources to execute.
However, you have to bear in mind what the actual use cases of your recursive code are and be very careful how you optimize them. Memoization can be a more powerful tool if a recursive function is called multiple times with different arguments, as its cache persists between calls, while iteration can be faster for recursive computations that are used less frequently. Always pay attention to your code and optimize for the cases you know or anticipate to be more common.

View File

@ -1,63 +0,0 @@
---
title: Code Anatomy - Writing high performance Python code
shortTitle: Performant Python code
type: story
language: python
tags: [list,performance]
cover: walking-on-top
excerpt: Writing efficient Python code can be tricky. Read how we optimize our list snippets to increase performance using a couple of simple tricks.
dateModified: 2021-11-07T16:34:37+03:00
---
Writing short and efficient Python code is not always easy or straightforward. However, it's often that we see a piece of code and we don't realize the thought process behind the way it was written. We will be taking a look at the [difference](/python/s/difference) snippet, which returns the difference between two iterables, in order to understand its structure.
Based on the description of the snippet's functionality, we can naively write it like this:
```py
def difference(a, b):
return [item for item in a if item not in b]
```
This implementation may work well enough, but doesn't account for duplicates in `b`. This makes the code take more time than necessary in cases with many duplicates in the second list. To solve this issue, we can make use of the `set()` method, which will only keep the unique values in the list:
```py
def difference(a, b):
return [item for item in a if item not in set(b)]
```
This version, while it seems like an improvement, may actually be slower than the previous one. If you look closely, you will see that `set()` is called for every `item` in `a` causing the result of `set(b)` to be evaluated every time. Here's an example where we wrap `set()` with another method to better showcase the problem:
```py
def difference(a, b):
return [item for item in a if item not in make_set(b)]
def make_set(itr):
print('Making set...')
return set(itr)
print(difference([1, 2, 3], [1, 2, 4]))
# Making set...
# Making set...
# Making set...
# [3]
```
The solution to this issue is to call `set()` once before the list comprehension and store the result to speed up the process:
```py
def difference(a, b):
_b = set(b)
return [item for item in a if item not in _b]
```
Another option worth mentioning in terms of performance is the use of a list comprehension versus `filter()` and `list()`. Implementing the same code using the latter option would result in something like this:
```py
def difference(a, b):
_b = set(b)
return list(filter(lambda item: item not in _b, a))
```
Using `timeit` to analyze the performance of the last two code examples, it's pretty clear that using list comprehension can be up to ten times faster than the alternative. This is due to it being a native language feature that works very similar to a simple `for` loop without the overhead of the extra function calls. This explains why we prefer it, apart from readability.
This pretty much applies to most mathematical list operation snippets, such as [difference](/python/s/difference), [symmetric_difference](/python/s/symmetric-difference) and [intersection](/python/s/intersection).

View File

@ -1,133 +0,0 @@
---
title: Common regular expressions
type: cheatsheet
language: javascript
tags: [string,regexp]
author: chalarangelo
cover: rocky-beach
excerpt: A collection of regular expressions that can be used to solve common problems.
dateModified: 2022-11-09T05:00:00-04:00
---
### Exact string match
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Add the string you want to match in-between the two anchors.
```js
const regexp = /^abc$/;
// Where 'abc' is the exact string you want to match
```
### Match empty string
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Do not add any characters in-between to match an empty string.
```js
const regexp = /^$/;
```
### Match whitespace sequences
- Use the `\s` meta-sequence to match any whitespace character, including spaces, tabs, newlines, etc.
- Use the `+` quantifier to match one or more occurrences of the previous character.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
```js
const regexp = /\s+/g;
```
### Match line breaks
- Depending on the environment, line breaks can be represented in different ways.
- Use the `\r` character to match carriage returns, the `\n` character to match newlines, and the `\r\n` sequence to match carriage returns followed by newlines.
- Add the global (`g`) and multiline (`m`) flags to match all occurrences of the pattern in the string.
```js
const regexp = /\r|\n|\r\n/gm;
```
### Match non-word characters
- Use negation (`^`) to match any character that is not a word character (`\w`) or a whitespace character (`\s`).
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
- Add the ignore case flag (`i`) to match both uppercase and lowercase characters.
```js
const regexp = /[^\w\s]/gi;
```
### Match alphanumeric, dashes and hyphens
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Use the `a-zA-Z0-9-` pattern to match any alphanumeric character, dashes and hyphens.
- Use the `+` quantifier to match one or more occurrences of the previous character.
- Particularly useful when matching URL slugs.
```js
const regexp = /^[a-zA-Z0-9-_]+$/;
```
### Match letters and whitespaces
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Use the `a-zA-Z\s` pattern to match any letter and whitespace character.
- Use the `+` quantifier to match one or more occurrences of the previous pattern.
```js
const regexp = /^[A-Za-z\s]+$/;
```
### Pattern not included
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Use a negative lookahead (`?!`) to match any character that is not followed by the pattern you want to exclude.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
- To ensure more than one pattern is not included, use the `|` character to separate them.
```js
const regexp = /^((?!(abc|bcd)).)*$/;
// Where 'abc' and 'bcd' are pattern you want to exclude
```
### Text inside brackets
- Use the `\(` and `\)` characters to match the opening and closing brackets, respectively.
- Use a capturing group between the two and exclude the closing parenthesis character.
- Use the `+` quantifier to match one or more characters, as needed.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
- Replace `\(` and `\)` with `\[` and `\]` to match square brackets and with `\{` and `\}` to match curly brackets.
```js
const regexp = /\(([^)]+)\)/g;
```
### Validate GUID/UUID
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Validate each segment of the GUID/UUID separately using numeric character ranges and quantifiers.
```js
const regexp = /^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/;
```
### Validate date format (DD/MM/YYYY)
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Validate each segment of the date separately using numeric character ranges and quantifiers.
- Alter the order of the segments and separators to match different formats.
```js
const regexp = /^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/;
```
### Chunk string into n-size chunks
- Use the `.{1,n}` quantifier to match any character between `1` and `n` times.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
```js
const regexp = /.{1,2}/g;
// Where '2' is the number of characters per chunk
```

View File

@ -1,119 +0,0 @@
---
title: JavaScript console.log() tips & tricks
type: story
language: javascript
tags: [browser,cheatsheet]
author: chalarangelo
cover: terminal
excerpt: Level up your JavaScript logging with these `console.log()` tips and tricks.
dateModified: 2021-06-12T19:30:41+03:00
---
Everyone uses the JavaScript console for logging or debugging every once in a while. But there is a lot more to the [console](https://developer.mozilla.org/en-US/docs/Web/API/Console) object than `console.log()`.
### Computed property names
ES6 computed property names are particularly useful, as they can help you identify logged variables by adding a pair of curly braces around them.
```js
const x = 1, y = 2, z = 3;
console.log({x, y, z}); // {x: 1, y: 2, z: 3}
```
### console.trace()
[`console.trace()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/trace) works the exact same as `console.log()`, but it also outputs the entire stack trace so you know exactly what's going on.
```js
const outer = () => {
const inner = () => console.trace('Hello');
inner();
};
outer();
/*
Hello
inner @ VM207:3
outer @ VM207:5
(anonymous) @ VM228:1
*/
```
### console.group()
[`console.group()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/group) allows you to group logs into collapsable structures and is particularly useful when you have multiple logs.
```js
console.group('Outer'); // Create a group labelled 'Outer'
console.log('Hello'); // Log inside 'Outer'
console.groupCollapsed('Inner'); // Create a group labelled 'Inner', collapsed
console.log('Hellooooo'); // Log inside 'Inner'
console.groupEnd(); // End of current group, 'Inner'
console.groupEnd(); // End of current group, 'Outer'
console.log('Hi'); // Log outside of any groups
```
### Logging levels
There are a few more logging levels apart from `console.log()`, such as [`console.debug()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/debug), [`console.info()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/info), [`console.warn()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/warn) and [`console.error()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/error).
```js
console.debug('Debug message');
console.info('Useful information');
console.warn('This is a warning');
console.error('Something went wrong!');
```
### console.assert()
[`console.assert()`](https://developer.mozilla.org/en-US/docs/Web/API/console/assert) provides a handy way to only log something as an error when an assertion fails (i.e. when the first argument is `false`), otherwise skip the log entirely.
```js
const value = 10;
console.assert(value === 10, 'Value is not 10!'); // Nothing is logged
console.assert(value === 20, 'Value is not 20!'); // Logs "Value is not 20!"
```
### console.count()
You can use [`console.count()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/count) to count how many times a piece of code has executed.
```js
Array.from({ length: 4 }).forEach(
() => console.count('items') // Call the counter labelled 'items'
);
/*
items: 1
items: 2
items: 3
items: 4
*/
console.countReset('items'); // Reset the counter labelled 'items'
```
### console.time()
[`console.time()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) gives you a quick way to check the performance of your code, but should not be used for real benchmarking due to its low accuracy.
```js
console.time('slow comp'); // Start the 'slow comp' timer
console.timeLog('slow comp'); // Log the value of the 'slow comp' timer
console.timeEnd('slow comp'); // Stop and log the 'slow comp' timer
```
### CSS
Last but not least, you can use the `%c` string substitution expression in `console.log()` to apply CSS to parts of a log.
```js
console.log(
'CSS can make %cyour console logs%c %cawesome%c!', // String to format
// Each string is the CSS to apply for each consecutive %c
'color: #fff; background: #1e90ff; padding: 4px', // Apply styles
'', // Clear any styles
'color: #f00; font-weight: bold', // Apply styles
'' // Clear any styles
);
```

View File

@ -1,57 +0,0 @@
---
title: What is the difference between cookies, local storage, and session storage?
shortTitle: Cookies, local storage, and session storage
type: question
language: javascript
tags: [browser,webdev]
author: chalarangelo
cover: three-vases
excerpt: Learn the difference between cookies, local storage and session storage and start using the correct option for your needs.
dateModified: 2021-06-12T19:30:41+03:00
---
### Cookies
Cookies store small amounts of data that has to be sent back to the server with subsequent requests and their expiration can be set from either server or client. They are primarily used for server-side reading.
- Capacity: 4KB
- Accessible from: Any window
- Expiration: Manually set
- Storage location: Browser and server
- Sent with requests: Yes
- Blockable by users: Yes
- Editable by users: Yes
### Local storage
Local storage stores a larger amount of data on the client's computer in a key-value pair format and has no expiration date. Data is never transferred to the server and is accessible via JavaScript and HTML5.
- Capacity: 10MB
- Accessible from: Any window
- Expiration: Never
- Storage location: Browser only
- Sent with requests: No
- Blockable by users: Yes
- Editable by users: Yes
### Session storage
Session storage stores a larger amount of data on the client's computer only for the current session, expiring the data on tab close. Data is never transferred to the server and is accessible client-side from the same tab.
- Capacity: 5MB
- Accessible from: Same tab
- Expiration: On tab close
- Storage location: Browser only
- Sent with requests: No
- Blockable by users: Yes
- Editable by users: Yes
| | Cookies | Local storage | Session storage |
| -- | -- | -- | -- |
| Capacity | 4KB | 10MB | 5MB |
| Accessible from | Any window | Any window | Same tab |
| Expiration | Manually set | Never | On tab close |
| Storage location | Browser and server | Browser only | Browser only |
| Sent with requests | Yes | No | No |
| Blockable by users | Yes | Yes | Yes |
| Editable by users | Yes | Yes | Yes |

View File

@ -1,55 +0,0 @@
---
title: How can I copy text to clipboard with JavaScript?
shortTitle: Copy text to clipboard
type: question
language: javascript
tags: [browser]
author: chalarangelo
cover: typing
excerpt: Learn how to programmatically copy text to clipboard with a few lines of JavaScript and level up your web development skills.
dateModified: 2022-01-11T09:47:54+03:00
---
### Asynchronous Clipboard API
A very common need when building websites is the ability to copy text to clipboard with a single button click. If you only need to support modern browsers, it's highly recommended to use the asynchronous [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). It's supported in all modern browsers and provides an easy and secure way to update the clipboard's contents.
All you have to do is ensure `Navigator`, `Navigator.clipboard` and `Navigator.clipboard.writeText` are truthy and then call `Clipboard.writeText()` to copy the value to clipboard. In case anything goes wrong, you can use `Promise.reject()` to return a promise that rejects immediately and keep the return type consistent.
```js
const copyToClipboard = str => {
if (navigator && navigator.clipboard && navigator.clipboard.writeText)
return navigator.clipboard.writeText(str);
return Promise.reject('The Clipboard API is not available.');
};
```
This is pretty much how the [copyToClipboardAsync snippet](/js/s/copy-to-clipboard-async) is implemented and should work across all modern browsers.
### Document.execCommand('copy')
While support for the Clipboard API is pretty high across the board, you might need a fallback if you have to support older browsers. If that's the case, you can use `Document.execCommand('copy')` to do so. Here's a quick step-by-step guide:
1. Create a` <textarea>` element to be appended to the document. Set its value to the string you want to copy to the clipboard.
2. Append the `<textarea>` element to the current HTML document and use CSS to hide it to prevent flashing.
3. Use `HTMLInputElement.select()` to select the contents of the `<textarea>` element.
4. Use `Document.execCommand('copy')` to copy the contents of the `<textarea>` to the clipboard.
5. Remove the `<textarea>` element from the document.
```js
const copyToClipboard = str => {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
};
```
Bear in mind that this method will not work everywhere, but only as a result of a user action (e.g. inside a `click` event listener), due to the way `Document.execCommand()` works.
There are a couple of other considerations, such as restoring the user's previous selection on the document, which can be easily handled with modern JavaScript. You can find the final code with these improvements implemented in the [copyToClipboard snippet](/js/s/copy-to-clipboard/).

View File

@ -1,29 +0,0 @@
---
title: What is CORS?
shortTitle: CORS explained
type: question
language: javascript
tags: [browser,webdev]
author: chalarangelo
cover: chill-surfing
excerpt: CORS (Cross-Origin Resource Sharing) trips up many developers, but it's pretty easy to wrap your head around.
dateModified: 2023-05-07T05:00:00-04:00
---
When it comes to HTTP, an **origin** is defined by several different aspects of a URL. As mentioned in a [previous article](/js/s/window-location-cheatsheet/), the origin is composed of the following:
- The **protocol** (e.g. `http` or `https`)
- The **hostname** (e.g. `30secondsofcode.org`)
- The **port** (e.g. `80` or `3000`)
As long as **all three** of these match, the browser considers the two URLs to be **same-origin**. If any of these aspects differ, the browser considers the two URLs to be **cross-origin**. It might be helpful to look at some examples of different origins to clarify:
- `http://30secondsofcode.org` and `https://www.30secondsofcode.org` (different protocols)
- `http://www.30secondsofcode.org` and `http://dev.30secondsofcode.org` (different hostnames)
- `https://30secondsofcode.org` and `https://30secondsofcode.org:3000` (different ports)
It's also important to note that the **path** (everything that comes after the hostname) is **not part of the origin**. This means that `https://30secondsofcode.org` and `https://30secondsofcode.org/articles` are considered to be the same origin.
When **CORS** (Cross-Origin Resource Sharing) is mentioned, it's usually in the context of **Same-Origin Policy**, a security feature implemented by web browsers. It **blocks web pages from making cross-origin requests** with the purpose of preventing malicious websites from making unauthorized requests to sensitive resources on other domains.
As this can be quite restrictive, CORS allows the server to specify which other domains are allowed to make requests to its resources. This is done through the use of **CORS headers**, `Origin` in the request and `Access-Control-Allow-Origin` in the response. This way, for example, API servers can allow requests from specific web pages, while still blocking requests from other domains.

View File

@ -1,37 +0,0 @@
---
title: How can I detect if Caps Lock is on with JavaScript?
shortTitle: Detect Caps Lock
type: question
language: javascript
tags: [browser,event]
author: chalarangelo
cover: keyboard
excerpt: If you need to check if Caps Lock is on when the user is typing in the browser, JavaScript's got you covered.
dateModified: 2021-06-12T19:30:41+03:00
---
Oftentimes, especially when creating password inputs, you need to check if the Caps Lock key is on and inform the user. You can do that using the [`KeyboardEvent.getModifierState()`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState) method with a value of `'CapsLock'`. This means that you have to listen for a keyboard event on an element in order to check the state of the Caps Lock key:
```html
<form>
<label for="username">Username:</label>
<input id="username" name="username">
<label for="password">Password:</label>
<input id="password" name="password" type="password">
<span id="password-message" style="display: none">Caps Lock is on</span>
</form>
```
```js
const el = document.getElementById('password');
const msg = document.getElementById('password-message');
el.addEventListener('keyup', e => {
msg.style = e.getModifierState('CapsLock')
? 'display: block'
: 'display: none';
});
```
As you can see from the above example, the `'keyup'` event is used on our element of choice to then call `KeyboardEvent.getModifierState()` and determine the state of the `'CapsLock'` key. `'keydown'` and `'keypress'` might also work. However, after testing on multiple devices, it seems that using `'keyup'` is the preferred method as it works better across different OSes and browsers.

View File

@ -1,50 +0,0 @@
---
title: "Tip: Refactoring your for...in loops to avoid ESLint warnings"
shortTitle: Refactoring for...in loops to avoid ESLint warnings
type: tip
language: javascript
tags: [array,iterator,eslint]
author: chalarangelo
cover: typing
excerpt: ESLint is a really useful tool, but sometimes it gets in the way. Learn how to refactor code to get rid of a common warning.
dateModified: 2021-06-12T19:30:41+03:00
---
ESLint is one of my tools of choice, but oftentimes it gets in the way of work, due to the way it prefers me to do things. One of the warnings I have seen more times than I care to admit is the following:
> for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.eslint(no-restricted-syntax)
And here are three refactoring options to deal with it:
### Object.keys()
`Object.keys()` has the exact same behavior as a `for...in` loop, so it can be used as a drop-in replacement:
```js
const data = [3, 4];
// Same as for (let k in data) console.log(k)
Object.keys(data).forEach(k => console.log(k));
// 0 1
```
### Object.values()
`Object.values()` is very similar to `Object.keys()`, but returns the values instead of the keys, which might be what you are really using the keys for:
```js
const data = [3, 4];
// Iterate over the values
Object.values(data).forEach(v => console.log(v));
// 3 4
```
### Object.entries()
Finally, if you need both key and value, `Object.entries()` has you covered:
```js
const data = [3, 4];
// Iterate over the data, returning key-value pairs
Object.entries(data).forEach(e => console.log(e[0], e[1]));
// [0, 3] [1, 4]
```

View File

@ -1,80 +0,0 @@
---
title: Flexbox Cheat Sheet
type: cheatsheet
language: css
tags: [layout,flexbox,cheatsheet]
author: chalarangelo
cover: frames
excerpt: Flexbox allows you to create fluid layouts easily. If you are constantly looking up how it works, this handy cheatsheet is all you need.
dateModified: 2021-06-12T19:30:41+03:00
---
### Container
- `display: flex` or `display: inline-flex`: creates a flex context (or an inline flex context) for direct children of this element
- `flex-direction` determines the main and cross axis for the container, valid values are:
- `row` (default): horizontal, in the direction of writing (left to right for English)
- `row-reverse`: horizontal, in the opposite direction of writing (right to left for English)
- `column`: vertical, top to bottom
- `column-reverse`: vertical, bottom to top
- `flex-wrap` determines if flex items will try to fit in one line, valid values are:
- `nowrap` (default): all flex items will be on one line
- `wrap`: flex items will wrap onto multiple lines, top to bottom
- `wrap-reverse`: flex items will wrap onto multiple lines, bottom to top
- `flex-flow`: shorthand combining `flex-direction` and `flex-wrap`
- Formal syntax: `flex-flow: <'flex-direction'> || <'flex-wrap'>`
- `justify-content` defines the alignment along the main axis, valid values are:
- `flex-start` (default): pack flex items from the start
- `flex-end`: pack flex items from the end
- `start`: pack items from the start
- `end`: pack items from the end
- `left`: pack items from the left
- `right`: pack items from the right
- `center`: pack items around the center
- `space-around`: distribute items evenly with equal space around them
- `space-between`: distribute items evenly with equal space between them
- `space-evenly`: distribute items evenly, ensuring equal space between any two items
- `stretch`: distribute items evenly, stretching auto-sized items to fit the container
- `align-items` defines the alignment along the cross axis, valid values are:
- `flex-start` (default): pack flex items from the start
- `flex-end`: pack flex items from the end
- `start`: pack items from the start
- `end`: pack items from the end
- `center`: pack items around the center
- `baseline`: align items based on their baselines
- `stretch`: stretch items to fill the container
- `align-content` defines the alignment of extra space along the cross axis, valid values are:
- `flex-start` (default): pack flex items from the start
- `flex-end`: pack flex items from the end
- `start`: pack items from the start
- `end`: pack items from the end
- `center`: pack items around the center
- `space-around`: distribute items evenly with equal space around them
- `space-between`: distribute items evenly with equal space between them
- `space-evenly`: distribute items evenly, ensuring equal space between any two items
- `stretch`: distribute items evenly, stretching auto-sized items to fit the container
![Diagram of Flexbox properties](./illustrations/flexbox-diagram.png)
### Items
- `flex-grow` determines how much the item can grow if necessary
- Accepts a single positive number (unitless), default value is `0`
- Specifies how much of the remaining space in the flex container should be assigned to the item
- The remaining space is the size of the flex container minus the size of all flex items' sizes together
- If all items have the same `flex-grow`, all items will receive an equal share of the remaining space
- If not all items have the same `flex-grow`, the remaining space is distributed according to the ratio defined by these values
- `flex-shrink` determines how much the items can shrink if necessary
- Accepts a single positive number (unitless), default value is `1`
- If the size of all flex items is larger than the flex container, items shrink to fit according to `flex-shrink`
- `flex-basis` determines the initial size of a flex item before the remaining space is distributed
- Can use any valid `width` value, intrinsic size values, `auto` (default) or `content`
- `auto` means "look at my `width` or `height` property", whereas `content` is used for automatic sizing
- `flex`: shorthand combining `flex-grow`, `flex-shrink` and `flex-basis`
- Formal syntax: `flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]`
- `align-self` allows the item to override the default `align-items` specified by the container
- Valid values are the same as those of the `align-items` property in the container
- `order` determines the ordering of the item
- Accepts an integer value
- Items in a container are sorted by ascending `order` value and then by their source code order
- Might cause accessibility issues if used incorrectly

View File

@ -1,52 +0,0 @@
---
title: Git aliases
type: cheatsheet
language: git
tags: [configuration,cheatsheet]
author: chalarangelo
cover: compass-1
excerpt: Increase your productivity by creating aliases for many common git operations.
dateModified: 2021-06-12T19:30:41+03:00
---
### Creating aliases
Use the command below to create aliases, replacing `<alias>` with the name of the alias and `<command>` with the command to be aliased:
```shell
git config --global alias.<alias> <command>
```
Additionally, you can use [edit the configuration file](/git/s/edit-config) and add many aliases all at once.
### Useful aliases
```editorconfig
[alias]
co = checkout
cob = checkout -b
coo = !git fetch && git checkout
br = branch
brd = branch -d
st = status
aa = add -A .
unstage = reset --soft HEAD^
cm = commit -m
amend = commit --amend -m
fix = commit --fixup
undo = reset HEAD~1
rv = revert
cp = cherry-pick
pu = !git push origin `git branch --show-current`
fush = push -f
mg = merge --no-ff
rb = rebase
rbc = rebase --continue
rba = rebase --abort
rbs = rebase --skip
rom = !git fetch && git rebase -i origin/master --autosquash
save = stash push
pop = stash pop
apply = stash apply
rl = reflog
```

View File

@ -1,21 +0,0 @@
---
title: "Tip: Create a commit with a different date"
shortTitle: Create a commit with a different date
type: tip
language: git
tags: [commit]
author: chalarangelo
cover: ice
excerpt: Ever needed to create a git commit with a different date? Here's a quick and easy way to do it.
dateModified: 2021-06-12T19:30:41+03:00
---
Sometimes, you might run into a situation where you need to create a commit with a different date than the current one. Luckily, you can handle this using `GIT_AUTHOR_DATE` and `GIT_COMMITTER_DATE`:
```shell
GIT_AUTHOR_DATE='Mon May 18 19:32:10 2020 -0400' \
GIT_COMMITTER_DATE='Mon May 18 19:32:10 2020 -0400'\
git commit -m 'Commit from the past'
```
As shown in the example above, you can set both values to any date you like and your code will be committed on that date. Note that the format for the values above is `'date +"%s %z"'`, also referred to as internal raw git format, but you can also use other formats, such as RFC 2822 (`'Mon, 18 May 2020 19:32:10 -0400'`), ISO 8601 (`'2020-05-18 19:32:10 -0400'`), local (`'Mon May 18 19:32:10 2020'`), short (`'2020-05-18'`) or relative (`5.seconds.ago`, `2.years.3.months.ago`, `'6am yesterday'`).

View File

@ -1,25 +0,0 @@
---
title: How does Git's fast-forward mode work?
shortTitle: Git fast-forward
type: question
language: git
tags: [branch]
author: chalarangelo
cover: boats
excerpt: Learn about Git's fast-forward mode works and its benefits when mergin branches, so you can decide if it's a good fit for you and your team.
dateModified: 2021-07-15T05:00:00-04:00
---
Merging a branch is one of the most common operations when working with Git. Depending on your team and projects you've been a part of, you might have heard of or even used Git's **fast-forward** mode when merging. Fast-forward mode is the default in Git, however GitHub will essentially override this by default and create a merge commit instead.
![Git fast forward explained](./illustrations/git-fast-forward.png)
### Fast-forward merge
As stated above, Git's default is to use fast-forward merge. It will take the commits from the branch being merged and place them at the tip of the branch you're merging into. This creates a **linear history**, which is also the main advantage of using fast-forward merge. If you want to emulate fast-forward merge on GitHub, you can use the "Rebase and merge" option.
### Non fast-forward merge
GitHub, on the other hand, uses non fast-forward merge by default. It will create a merge commit at the tip of the branch you're merging into, optionally referencing the branch being merged in the commit message. This has the advantage of **keeping track of branches** more explicitly than fast-forward merge. If you want to get the same behavior in a Git terminal, you can use the `--no-ff` flag.
As a side note, you can configure the default Git merge behavior, using `git config`. To learn how to do so, you can take a look at the [relevant snippet](/git/s/disable-fast-forward).

View File

@ -1,27 +0,0 @@
---
title: "Tip: How to add multiple authors to a commit"
shortTitle: Add multiple authors to a commit
type: tip
language: git
tags: [github,programming,webdev]
author: chalarangelo
cover: book-chair
excerpt: Learn how to add multiple authors to a git commit with this quick and easy tip.
dateModified: 2021-06-12T19:30:41+03:00
---
You can add multiple authors to a git commit, by adding one or more `Co-authored-by` trailers to the commit's message:
```shellsession
$ git commit -m "Refactor usability tests.
>
>
Co-authored-by: name <name@example.com>
Co-authored-by: another-name <another-name@example.com>"
```
### Notes:
- To correctly attribute a commit to a co-author, you must use the email associated with their GitHub account.
- If a person's email is private, you can use their GitHub-provided `no-reply` email.
- Leave one or preferably two empty lines before any `Co-authored-by` trailers.

View File

@ -1,22 +0,0 @@
---
title: Mocking global object methods in Jest
type: story
language: javascript
tags: [testing]
author: chalarangelo
cover: trippy-chemicals
excerpt: Testing your code is important, but mocking can be tricky at times. Here's a quick guide on how to mock global object methods in Jest.
dateModified: 2022-03-27T05:00:00-04:00
---
Testing is a big part of the development process. It's also where a lot of mistakes can be overlooked, which can pile up and lead to hard-to-debug issues. A common problem is poorly-written mocks, especially regarding global objects and their methods. Let's take a look at how to mock global object methods in Jest.
When mocking global object methods in Jest, the optimal way to do so is using the `jest.spyOn()` method. It takes the object and name of the method you want to mock, and returns a mock function. The resulting mock function can then be chained to a mocked implementation or a mocked return value. For example:
```js
jest.spyOn(Math, 'random').mockReturnValue(0.123456789);
jest.spyOn(Date, 'now').mockReturnValue('123456789');
```
In this example, we mock two global object methods and return a fixed value. You could as easily mock their implementation using `mockFn.mockImplementation()`. Using either of these options allows you to get predictable values from the mocked methods. This comes in especially handy when working, for example, with `Math.random()` or `Date.now()`.

View File

@ -1,21 +0,0 @@
---
title: "Tip: Debugging Node.js using Chrome Developer Tools"
shortTitle: Debug Node.js with Chrome Developer Tools
type: tip
language: javascript
tags: [node,debugging]
author: chalarangelo
cover: bug
excerpt: Did you know you can use Chrome Developer Tools to debug your Node.js code? Find out how in this short guide.
dateModified: 2021-06-12T19:30:41+03:00
---
Node.js can be debugged using Chrome Developer Tools since `v6.3.0`. Here's a quick guide on how to do this:
1. Download and install Node.js `v6.3.0` or newer, if you don't already have it installed on your machine.
2. Run node with the `--inspect-brk` flag (e.g. `node --inspect-brk index.js`).
3. Open `about:inspect` in a new tab in Chrome. You should see something like the screenshot below.
4. Click `Open dedicated DevTools for Node` to open a new window connected to your Node.js instance.
5. Use the Developer Tools to debug your Node.js application!
![about:inspect page](./illustrations/chrome-debug-node.png)

View File

@ -1,195 +0,0 @@
---
title: Create a static file server with Node.js
shortTitle: Node.js static file server
type: story
language: javascript
tags: [node,server]
author: chalarangelo
cover: man-cup-laptop
excerpt: Create your own static file server with Node.js in just 70 lines of code.
dateModified: 2022-06-05T05:00:00-04:00
---
### A simple static file server
One of the simplest beginner backend projects you can create is a static file server. In its simplest form, a static file server will listen for requests and try to match the requested URL to a file on the local filesystem. Here's a minimal example of that in action:
```js
const fs = require('fs');
const http = require('http');
http.createServer((req, res) => {
fs.readFile(__dirname + req.url, (err, data) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data);
}
});
}).listen(8000);
```
In this code example, we're using the `fs` module to read the file at `__dirname + req.url`. If the file doesn't exist, we'll return a `404` error. Otherwise, we'll return the file. The `http` module is used to create the server that listens on port `8000`.
In theory, one could stop here and have a very basic static file server. However, there are a few considerations that could be taken into account. Let's explore them one by one, and see how we can address them.
### Modularity
First and foremost, we don't necessarily want to serve files from the same directory as our Node.js server. To address this problem, we would have to change the directory `fs.readFile()` looks for the file in. To accomplish this, we can specify a directory to serve files from and use the `path` module to resolve files from that directory. This way, we can also better handle different operating systems and environments.
Here's a short snippet on how to resolve a file path using the `path` module:
```js
const fs = require('fs');
const path = require('path');
const directoryName = './public';
const requestUrl = 'index.html';
const filePath = path.join(directoryName, requestUrl);
fs.readFile(filePath, (err, data) => {
// ...
});
```
### Security
Our next concern is security. Obviously, we don't want users prying around our machine unauthorized. Currently, it's not impossible to get access to files outside of the specified root directory (e.g. `GET /../../../`). To address this, we can use the `path` module again to check if the requested file is inside the root directory.
```js
const path = require('path');
const directoryName = './public';
const root = path.normalize(path.resolve(directoryName));
const requestUrl = 'index.html';
const filePath = path.join(root, fileName);
const isPathUnderRoot = path
.normalize(path.resolve(filePath))
.startsWith(root);
```
Similarly, we can ensure that users don't get access to sensitive files by checking the file type. For this to work, we can specify an array or object of supported file types and check the file's extension using the `path` module once again.
```js
const path = require('path');
const types = ['html', 'css', 'js', 'json'];
const requestUrl = 'index.html';
const extension = path.extname(requestUrl).slice(1);
const isTypeSupported = types.includes(extension);
```
### Omitting the HTML extension
A staple of most websites is the ability to omit the file extension from the URL when requesting an HTML page. It's a small quality of life improvement that users expect and it would be really nice to add to our static file server.
This is where things get a little tricky. To provide this functionality, we need to check for missing extensions and look up the appropriate HTML file. Bear in mind, though, that there are two possible matches for a URL such as `/my-page`. This path can either be matched by `/my-page.html` or `my-page/index.html`. To deal with this, we'll prioritize one over the other. In our case, we'll prioritize `/my-page.html` over `my-page/index.html`, but it's pretty easy to swap them the other way round.
To implement this, we can use the `fs` module to check if one of them exists and handle things appropriately. A special case would also need to be added for the root url (`/`) to match it to the `index.html` file.
```js
const fs = require('fs');
const path = require('path');
const directoryName = './public';
const root = path.normalize(path.resolve(directoryName));
const extension = path.extname(req.url).slice(1);
let fileName = requestUrl;
if (requestUrl === '/') fileName = 'index.html';
else if (!extension) {
try {
fs.accessSync(path.join(root, requestUrl + '.html'), fs.constants.F_OK);
fileName = requestUrl + '.html';
} catch (e) {
fileName = path.join(requestUrl, 'index.html');
}
}
```
### Final touches
After implementing all of the above, we can put everything together to create a static file server with all the functionality we need. I'll throw in a couple of finishing touches, such as logging requests to the console and handling a few more file types, and here's the final product:
```js
const fs = require('fs');
const http = require('http');
const path = require('path');
const port = 8000;
const directoryName = './public';
const types = {
html: 'text/html',
css: 'text/css',
js: 'application/javascript',
png: 'image/png',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
gif: 'image/gif',
json: 'application/json',
xml: 'application/xml',
};
const root = path.normalize(path.resolve(directoryName));
const server = http.createServer((req, res) => {
console.log(`${req.method} ${req.url}`);
const extension = path.extname(req.url).slice(1);
const type = extension ? types[extension] : types.html;
const supportedExtension = Boolean(type);
if (!supportedExtension) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
return;
}
let fileName = req.url;
if (req.url === '/') fileName = 'index.html';
else if (!extension) {
try {
fs.accessSync(path.join(root, req.url + '.html'), fs.constants.F_OK);
fileName = req.url + '.html';
} catch (e) {
fileName = path.join(req.url, 'index.html');
}
}
const filePath = path.join(root, fileName);
const isPathUnderRoot = path
.normalize(path.resolve(filePath))
.startsWith(root);
if (!isPathUnderRoot) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
return;
}
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
} else {
res.writeHead(200, { 'Content-Type': type });
res.end(data);
}
});
});
server.listen(port, () => {
console.log(`Server is listening on port ${port}`);
});
```
Not too bad, right? In just 70 lines of code, we managed to create a pretty decent static file server without using anything but core Node.js APIs.

View File

@ -1,136 +0,0 @@
---
title: Introduction to the Node.js test module
shortTitle: Node.js test module introduction
type: story
language: javascript
tags: [node,testing]
author: chalarangelo
cover: contemporary-desk
excerpt: The Node.js test module is a new testing tool that's still in its early stages. Learn more about it in this short introduction.
dateModified: 2023-04-30T05:00:00-04:00
---
A little while back, I stumbled upon the [Node.js test module](https://nodejs.org/docs/latest-v18.x/api/test.html). Having tried various JavaScript testing tools in recent years, I decided to set some time aside to test it and see how it works.
_Before you read any further, note that at the time of writing (April, 2023), this module is still **experimental** and is likely to change in future releases. While **not recommended for production use**, it's still worth learning about as it might come in handy later down the line._
So, how does the Node.js test module compare to the likes of Jest and Vitest? As expected, it's less feature-rich than other tools, which is understandable given that it's still in its early stages. However, the core functionality is there and it's very easy to use and set up as it **doesn't require third-party dependencies**.
Before you can use the module, you'll have to **import** it. You'll most likely have to import the **assertion module** as well. Here's how to import both:
```js
import test from 'node:test';
import assert from 'node:assert/strict';
```
Additionally, if you want to use methods such as `describe` or `it` from the test module, you'll have to import them as well. For example:
```js
import { describe, it } from 'node:test';
```
Now that you have the module imported, you can **start writing tests**. The test module provides a `test` function that takes two arguments in its simplest form:
- `name`: a string describing the test
- `fn`: a function containing the test logic
Let's look at an example:
```js
import test from 'node:test';
import assert from 'node:assert/strict';
test('my test', () => {
assert.strictEqual(1, 1);
});
```
This is pretty simple, but how does one **run the tests from the command line**? Contrary to most other tools, you don't need to create a new entry under your `package.json`'s `scripts` object. Instead, you can simply run the `node --test` command:
```shell
node --test
```
Running this command will match certain **file patterns**, such as files ending in `.test.js` and files under the `test` directory. You can read more about this in the [documentation](https://nodejs.org/docs/latest-v18.x/api/test.html#test-runner-execution-model). Additionally, you can manually specify one or more space-separated file patterns to run tests from:
```shell
node --test my-example.test.js
```
These basic instructions might cover some simple tests, but no testing tool is complete without more advanced features. Two important testing staples are **setup and teardown functions**. The test module provides `beforeEach` and `afterEach` functions that run before and after each test, respectively.
Note that you can import these functions and use them, but there's also another way. By making the test **asynchronous** and passing an argument to the test function, you can use the `beforeEach` and `afterEach` functions as methods of the test object. I've found this to be a bit more convenient, so I'll be using this method in the examples below. Here's what that looks like:
```js
import test from 'node:test';
import assert from 'node:assert/strict';
test('my test', async t => {
let a;
t.beforeEach(() => {
a = 1;
});
t.afterEach(() => {
a = 0;
});
await t.test('my subtest', () => {
assert.strictEqual(a, 1);
});
});
```
While all of this sounds pretty good, you might be wondering what a **real-world example** looks like. I cooked up a little function that mutates an array of numbers and returns a value just to demonstrate. The logic is not particularly complex or interesting, but it's enough to take the module for a spin. Let's have a look:
```js
import test, { describe } from 'node:test';
import assert from 'node:assert/strict';
const doubleAndSum = (arr, mod = 0) => {
let sum = 0;
Object.entries(arr).forEach(([i, v]) => {
if (v % 2 === mod) arr[i] = v * 2;
else sum += v;
});
return sum;
};
describe('doubleAndSum', () => {
let arr, sum;
test('when mod is 0', async t => {
t.beforeEach(() => {
arr = [1, 2, 3];
sum = doubleAndSum(arr, 0);
});
await t.test('sums the even values', () => {
assert.equal(sum, 4);
});
await t.test('doubles the even values', () => {
assert.equal(arr[1], 4);
});
});
test('when mod is 1', async t => {
t.beforeEach(() => {
arr = [1, 2, 3];
sum = doubleAndSum(arr, 1);
});
await t.test('sums the even values', () => {
assert.equal(sum, 2);
});
await t.test('doubles the odd values', () => {
assert.equal(arr[0], 2);
assert.equal(arr[2], 6);
});
});
});
```
And there you have it! Hopefully, by now you have a pretty good idea of what the Node.js test module is all about. There's a lot more to it, such as **coverage** reporting, **watch mode** and **mocking**, if you feel like digging deeper. You can find more information in the [documentation](https://nodejs.org/docs/latest-v18.x/api/test.html).

View File

@ -1,22 +0,0 @@
---
title: "Tip: Improve scroll listener performance"
shortTitle: Improve scroll listener performance
type: tip
language: javascript
tags: [browser,event]
author: chalarangelo
cover: chill-surfing
excerpt: Scroll listeners can easily become a performance bottleneck for your web application. Here's how to fix that.
dateModified: 2023-03-07T05:00:00-04:00
---
When working with scroll listeners in JavaScript, one can often run into performance issues. This is because scroll listeners are triggered on **every single scroll event**, which can be quite frequent. Most of the time, such listeners are used for infinite scrolling and lazy loading, meaning that the scroll event won't be intercepted. As such, `Event.preventDefault()` will not be called, guving us an optimization opportunity.
```js
window.addEventListener('scroll', () => {
// Do something
// Can't use `preventDefault` here
}, { passive: true });
```
As demonstrated in this code snippet, setting the `passive` option to `true` will enable certain **performance optimizations** in the browser. This way, the browser will know that it can safely skip the event queue and execute the scroll listener immediately. The result is a much smoother experience for the user, as the scroll event will be handled immediately, instead of being queued and handled later.

View File

@ -1,17 +0,0 @@
---
title: "Tip: The perfect duration for CSS transitions"
shortTitle: CSS transition duration
type: tip
language: css
tags: [interactivity,visual,animation]
author: chalarangelo
cover: perfect-timing
excerpt: Learn how to make your CSS transitions feel perfect when users interact with elements on the page with this simple tip.
dateModified: 2021-06-12T19:30:41+03:00
---
We have all experienced a website interaction that feels sluggish or otherwise off on account of poor transition or animation duration and timing. However, there is a very simple "golden rule" to help you avoid this poor user experience, called **Doherty Threshold:**
> Productivity soars when a computer and its users interact at a pace (<400ms) that ensures that neither has to wait on the other.
The **magic number** of `0.4s` sounds like a very reasonable threshold, but take a look at any of your favorite websites and you'll notice that most `transition-duration` or `animation-duration` values are closer to `0.3s`. This might have something to do with our more recent expectation of what a fast interaction should feel like - after all the relevant research paper was published in 1982.

View File

@ -1,62 +0,0 @@
---
title: Regular Expressions Cheat Sheet
type: cheatsheet
language: javascript
tags: [string,regexp,cheatsheet]
author: chalarangelo
cover: tools
excerpt: Regular expressions are a very useful tool. Save this cheatsheet for when you need to look up their syntax and speed up your development.
dateModified: 2021-06-12T19:30:41+03:00
---
### Anchors
- `^`: start of the string or the start of a line in a multiline pattern
- `$`: end of the string or the end of a line in a multiline pattern
- `\b`: word boundary
- `\B`: not word boundary (opposite of `\b`)
Note: Anchors are non-quantifiable (i.e. cannot be followed by a quantifier).
### Character sequences
- `.`: any character except line breaks
- `\w`: any word character
- `\W`: any non-word character (opposite of `\w`)
- `\s`: any whitespace character
- `\S`: any non-whitespace character (opposite of `\s`)
- `\d`: any digit character
- `\D`: any non-digit character (opposite of `\d`)
- `[abc]`: a single character in the given set (here `a`, `b` or `c`)
- `[^abc]`: a single character not in the given set (opposite of `[abc]`)
- `[a-z]`: a single character in the given range (here between `a` and `z` inclusive)
- `[^a-z]`: a single character not in the given range (opposite of `[a-z]`)
- `[a-zA-Z]`: a single character in either of the given ranges
Note: Use `\` to escape special characters (e.g. `\`, `/`, `[`, `]`, `(`, `)`, `{`, `}` etc.).
### Quantifiers
- `a?`: zero or one of `a` (equal to `a{0,1}`)
- `a*`: zero or more of `a` (equal to `a{0,}`)
- `a+`: one or more of `a` (equal to `a{1,}`)
- `a{3}`: exactly 3 of `a`
- `a{3,}`: 3 or more of `a`
- `a{3,5}`: between 3 and 5 of `a` (inclusive)
Note: `a` is any valid quantifiable expression.
### Groups
- `(ab)`: match and capture everything enclosed (here exactly `ab`)
- `(a|b)`: match and capture either one character (here `a` or `b`)
- `(?:ab)`: match everything enclosed, without capturing
### Flags
- `g`: Global
- `m`: Multiline
- `i`: Case insensitive
- `u`: Unicode
Note that this cheatsheet is meant only as a starting point and is by no means a complete guide to all the features and nuances of regular expressions. You can also read [6 JavaScript Regular Expression features you can use today](/blog/s/6-javascript-regexp-tricks) for a deeper dive into some more advanced features.

View File

@ -1,20 +0,0 @@
---
title: "Tip: Select the focused DOM element"
shortTitle: Focused DOM element
type: tip
language: javascript
tags: [browser]
author: chalarangelo
cover: horse-sunset
excerpt: Here's a quick and easy way to select the currently focused DOM element in JavaScript.
dateModified: 2022-10-23T05:00:00-04:00
---
Finding the currently focused DOM element is trivial in modern CSS, using the `:focus` selector. You can also use it in JavaScript, in combination with `Document.querySelector()` to find the focused element. Yet, there's an even easier way to get the currently focused element in JavaScript, using the `Document.activeElement` property.
```js
const focusedElement = document.activeElement;
// `focusedElement` is the currently focused element
```
Note that focusable elements vary depending on browser and operating system. Additionally, you should remember that focus and selection (i.e. content highlighting) are not the same thing.

View File

@ -1,32 +0,0 @@
---
title: "Tip: Set up Python 3 and pip 3 as default"
shortTitle: Python 3 and pip 3 setup
type: tip
language: python
tags: [setup]
author: chalarangelo
cover: avocado-slices
excerpt: A very common problem when working with Python is having to remember the correct version. Luckily, there's an easy fix for that.
dateModified: 2021-06-12T19:30:41+03:00
---
One of the most common headaches when working with Python is having to remember to use Python 3.x instead of Python 2.x. Luckily, it's really easy to setup Python 3 and pip 3 as the defaults. You first need to figure out where each one is installed using the `which` command:
```shell
which python3 # /usr/local/bin/python3
which pip3 # /usr/local/bin/pip3
```
Make a note of each response, so that you can add the paths as aliases to your shell environment's configuration file. Then, you can use `echo` to add a line for each one to either `.zshrc` or `.bashrc` depending on your environment:
```shell
# Linux or other bash environment
echo "alias python=/usr/local/bin/python3" >> ~/.bashrc
echo "alias pip=/usr/local/bin/pip3" >> ~/.bashrc
# Mac OS or other zsh environment
echo "alias python=/usr/local/bin/python3" >> ~/.zshrc
echo "alias pip=/usr/local/bin/pip3" >> ~/.zshrc
```
And you're all done! `python` and `pip` are both mapped to their 3.x versions,

View File

@ -1,160 +0,0 @@
---
title: Testing React components that update asynchronously with React Testing Library
shortTitle: Asynchronous component update testing
type: story
language: react
tags: [testing,event]
cover: colorful-lounge
excerpt: Testing React components that update asynchronously is pretty common. Learn how to deal with common issues and speed up your testing.
dateModified: 2021-11-07T16:34:37+03:00
---
### Components that update asynchronously
Recently, while working on a side-project, we started using the [React DnD library](https://react-dnd.github.io/react-dnd), as we wanted to implement a multi-container drag and drop system with cards.
After spending the better part of a day implementing the functionality, we decided to add some tests to ensure everything will keep working as expected. In the aforementioned project, we use [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) to write tests for our components.
While testing the drag functionality, we came across a very stubborn test. Here's a simplified version of our `Card` component:
```jsx
import React from 'react';
import { useDrag } from 'react-dnd';
const Card = ({
card: {
id,
title
}
}) => {
const [style, drag] = useDrag({
item: { id, type: 'card' },
collect: monitor => ({
opacity: monitor.isDragging() ? 0 : 1
})
});
return (
<li className="card" id={id} ref={drag} style={style}>
{title}
</li>
);
};
```
And here's the test we were trying to write originally:
```jsx
import React from 'react';
import { fireEvent } from '@testing-library/react';
import Card from './components/Card';
// This a little helper we have written to connect to redux and react-dnd
import renderDndConnected from './test_utils/renderDndConnected';
describe('<Card/>', () => {
let card;
beforeEach(() => {
const utils = renderDndConnected(
<Card card={{ id: '1', title: 'Card' }} />
);
card = utils.container.querySelector('.card');
});
it('initial opacity is 1', () => {
expect(card.style.opacity).toEqual('1');
});
describe('when drag starts', () => {
beforeEach(() => {
fireEvent.dragStart(card);
});
it('opacity is 0', () => {
expect(card.style.opacity).toEqual('0');
});
});
});
```
### The dreaded `act(...)` warning
While the test was obviously not working, the console was constantly nagging about wrapping the test in `act()`:
```
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser.
```
This message wasn't very helpful in identifying the underlying issue. The only thing it highlighted was that the test didn't update the component style immediately. There were pending updates after the test completed. To put it plainly, the test was failing because the `dragStart` event didn't immediately update the `Card` components' style (i.e. set the new `opacity`).
As a side note, the `Card` component is connected to Redux, which might relate to the issue, but it would most likely happen even without Redux. That's probably due to the fact that `collect` takes some amount of time to run and send an update to the component.
### Solving the issue
Digging deeper, we found that apart from `act()`, there are also other options, such as `waitFor()` and `waitForDomChange()`. These seem more intuitive simply because of the name and way they're written (using either `async await` or promises). However, `waitForDomChange()` didn't work properly for our case and our version of `react-testing-library` (which shipped with `react-scripts`) was outdated and did not export `waitFor()`, which took us a good half an hour to figure out.
After updating `react-testing-library`, we were still not ready to go, as the console started displaying the following error:
```
TypeError: MutationObserver is not a constructor
```
This required some searching, which eventually led us to [this issue](https://github.com/testing-library/react-testing-library/issues/662) which helped us figure out that a solution was to replace the `test` script in our `package.json` with this line:
```json
{
// ...
"scripts": {
"test": "react-scripts test --env=jsdom-fourteen"
// ...
}
}
```
Now to finally write a test that works! As mentioned above, we opted to use `waitFor()` from `react-testing-library`, which was actually the only change to the original testing code, except for the dependency bump and the script change described above. Here's the test after making the necessary changes:
```jsx
import React from 'react';
import { fireEvent, waitFor } from '@testing-library/react';
// This a little helper we have written to connect to redux and react-dnd
import renderDndConnected from './test_utils/renderDndConnected';
import Card from './components/Card';
describe('<Card/>', () => {
let card;
beforeEach(() => {
const utils = renderDndConnected(
<Card card={{ id: '1', title: 'Card' }} />
);
card = utils.container.querySelector('.card');
});
it('initial opacity is 1', () => {
expect(card.style.opacity).toEqual('1');
});
describe('when drag starts', () => {
beforeEach(() => {
fireEvent.dragStart(card);
});
it('opacity is 0', async() => {
await waitFor(() => expect(card.style.opacity).toEqual('0'));
});
});
});
```
### Summary
- A message about code that causes React state updates not being wrapped in `act(...)` might indicate that a component updated after the test ended.
- Using `waitFor()` can solve the issue by making tests asynchronous, but you might need to bump your `react-testing-library` version if you are using older versions of `react-scripts`.
- If you see errors related to `MutationObserver`, you might need to change your `test` script to include `--env=jsdom-fourteen` as a parameter.

View File

@ -1,37 +0,0 @@
---
title: Testing React portals
shortTitle: Portal testing
type: story
language: react
tags: [testing]
author: chalarangelo
cover: portal-timelapse
excerpt: Testing React components that use portals can be difficult until you understand what you really need to be testing.
dateModified: 2022-03-13T05:00:00-04:00
---
Testing React components can get pretty complicated, especially when dealing with portals. While they seem intimidating, what they are in essence is a way to render a component in a different place in the DOM. Apart from that, when writing tests, one should avoid testing framework internals. This obviously applies to React internals as well.
Putting these two points together, all we really care about when testing React portals is if the **portalized output** is correct. Based on that, mocking portals shouldn't be all that hard. We just need to mock `ReactDOM.createPortal()` to render the input element in place. Here's what that looks like in Jest:
```jsx
describe('MyComponent', () => {
beforeAll(() => {
ReactDOM.createPortal = jest.fn((element, node) => {
return element;
});
});
afterEach(() => {
ReactDOM.createPortal.mockClear();
});
it('should render correctly', () => {
const component = renderer.create(<MyComponent>Hello World!</MyComponent>);
expect(component.toJSON()).toMatchSnapshot();
});
});
```
This kind of mock will work in most cases and it will help ensure that the portalized output is correct. It comes with some caveats, though. First and foremost, the tested DOM is different from the actual DOM of the application. This can make tests less robust, reducing confidence in their result. Secondly, issues that might arise from the use of portals, such as the portal node missing, must be tested separately.

View File

@ -1,70 +0,0 @@
---
title: Testing Redux-connected components with React Testing Library
shortTitle: Redux-connected components testing
type: story
language: react
tags: [testing]
author: chalarangelo
cover: sparkles
excerpt: Testing Redux-connected components is pretty common. Learn how to use this simple utility function to speed up your testing.
dateModified: 2021-11-07T16:34:37+03:00
---
Testing Redux-connected components with React Testing Library is a very common scenario. However, it might be a little complicated without the proper tools and you could end up repeating yourself. This is especially true when writing the boilerplate to connect to your redux store.
Here's a simple utility function adapted from [React Testing Library's docs on the subject](https://testing-library.com/docs/example-react-redux) to help you speed up your testing:
```jsx
// src/test/utils/renderConnected.js
import React from 'react';
import { render } from '@testing-library/react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
// Replace this with the appropriate imports for your project
import { reducer, reducerInitialState } from './myReduxStore';
const renderConnected = (
ui, {
initialState = reducerInitialState,
store = createStore(reducer, initialState),
...renderOptions
} = {}
) => {
const Wrapper = ({ children }) => (
<Provider store={store}>{children}</Provider>
);
return render(ui, { wrapper: Wrapper, ...renderOptions});
};
export default renderConnected;
```
This utility uses the `createStore` function and the `<Provider>` component to wrap a redux-connected component to the passed state. Then it uses React Testing Library's `render` to finally render the result.
Remember to replace `import` statements with the appropriate files and exports to set up the utility as necessary. After creating the utility function and saving it in an appropriate file, you can use it like this:
```jsx
// src/test/SomeComponent.test.jsx
import React from 'react';
// Replace this with the appropriate location of your component
import SomeComponent from 'src/components/SomeComponent';
// Replace this with the appropriate location of your testing utility
import renderConnected from 'src/test/utils/renderConnected';
describe('<SomeComponent/>', () => {
let wrapper, getByText;
const initialState = {
// ... Add your initial testing state here
};
beforeEach(() => {
const utils = renderConnected(<SomeComponent />, { initialState });
wrapper = utils.container;
getByText = utils.getByText;
});
it('renders the component', () => {
expect(wrapper.querySelector('.some-component')).toBeInTheDocument();
});
});
```

View File

@ -1,55 +0,0 @@
---
title: An approach to testing stateful React components
shortTitle: Stateful component testing
type: story
language: react
tags: [testing]
author: chalarangelo
cover: lake-trees
excerpt: Testing stateful React components is not difficult, but did you know there's a solution that doesn't involve testing state directly?
dateModified: 2021-06-12T19:30:41+03:00
---
Some time ago, I was tasked with writing tests for a handful of React components, an otherwise mundane and uninspiring task, that somehow ended with a "Eureka!" moment for me. The specifics of the project and its components are of little importance, however the key detail is that I was working with stateful React components that are used daily by a large team and, as such, are refactored and updated quite often.
My initial approach consisted of writing some simple tests, such as checking if the component is rendered properly and if certain events fire appropriately. In doing so, I was comparing state directly with the result I was expecting, having the component's code right next to my assertions. Of course, this isn't bad by anyone's standards, but for a codebase with many moving parts, it is not the greatest idea. Let me show you an example why:
```js
context('the component is initialized in a collapsed state', function() {
let wrapper;
beforeEach(function(){
wrapper = mount(<StatefulComponent />);
});
it('component state.expanded is false', function() {
expect(wrapper.state('expanded')).to.be.false;
});
});
```
In this test, we check if the component's state has `expanded` equal to `false`. Our test will pass, as long as this simple condition is true. It's a very simple test that should be easy to understand even for someone completely unfamiliar with the codebase.
However, over time the component's implementation might change. What happens if `expanded` in our state ends up meaning something different? Or worse yet, if it isn't reflected the same way in the interface?
Enter my "Eureka!" moment:
> The application's UI should always be considered the result of combining the component's props and state.
The above statement implies that a component's state can be considered a black box while testings, an abstraction layer that should not be accessed unless absolutely necessary. So, instead of the test presented above, we should be doing something more like this:
```js
context('the component is initialized in a collapsed state', function() {
let wrapper;
beforeEach(function(){
wrapper = mount(<StatefulComponent />);
});
it('component does not have the expanded class', function() {
expect(wrapper.find('div').hasClass('expanded')).to.be.false;
});
});
```
Our test is still easy to read and understand, but it's a better test in general.
By directly checking the DOM instead of the component's state, we provide information about the component's output to future code authors, instead of asking them to keep the existing implementation intact. It seems like a better way to document the component and it's easier to track future changes should someone refactor the UI in such a way that the DOM representation of the component is altered.

View File

@ -1,49 +0,0 @@
---
title: The case for trailing commas in JavaScript
shortTitle: Trailing commas in JavaScript
type: story
language: javascript
tags: [webdev]
author: chalarangelo
cover: contemporary-desk
excerpt: Trailing commas are not without controversy. Here's why I think you should use them.
dateModified: 2023-03-12T05:00:00-04:00
---
JavaScript's syntactic features are notorious for sparking debates among developers, with semicolons and tabs vs. spaces being the most common examples. However, trailing commas are a feature that isn't discussed as much, even though I believe there's merit to using them.
JavaScript has allowed trailing commas in array and object literals since the **ES5 specification**. Adding a trailing comma is completely valid and will not cause any errors or alter the code in any way. For example, the two statements below are exactly the same:
```js
[1, 2, 3]; // [1, 2, 3]
[1, 2, 3,]; // [1, 2, 3]
```
This example might look strange, and indeed it's not necessarily the prettiest sight. Where trailing commas start to feel more useful is when working with multiline arrays or objects. For example, consider the following object:
```js
const obj = {
a: 1,
b: 2,
c: 3
};
```
There's nothing wrong with this code snippet, except if we want to make a change to the object. In that case, we will have to add a comma to the last line, before adding a new property. A similar issue arises if we reorder the properties. In that case, a comma might end up missing from the line that was previously last. Finally, if we want consistency, removing the last property will also require the removal of the comma from the line before it.
None of these scenarios are uncommon, as you are well aware. Even more so, these start becoming a little more annoying when you factor in **version control**. A single property addition will require two lines to be altered. This can make diffs harder to read and review, and can also cause merge conflicts more often than you think.
I think it's clear by now that trailing commas can improve **code readability** and increase **diff clarity**. This is not a new idea and has been suggested by popular style guides for years, especially in multiline array and object literals.
As with most things, ESLint has a rule for this. The `comma-dangle` rule can be used to enforce trailing commas in both single-line and multiline situations. You can even go further and customize it for different types of literals, such as arrays, objects, functions, and imports/exports. My personal recommendation is to **enforce trailing commas only for multiline literals**:
```js
{
"rules": {
// Other rules ...
"comma-dangle": ["error", "always-multiline"]
}
}
```
This way you get all the benefits of trailing commas, without having to look at the potentially confusing trailing commas in single-line literals. As a final note, bear in mind that style decisions should always be discussed with your team and agreed upon.

View File

@ -1,62 +0,0 @@
---
title: Typographic scale basics
type: story
language: css
tags: [webdev,typography]
author: chalarangelo
cover: typography
excerpt: Typography might seem intimidating, but you can quickly and easily create a simple typographic scale with this basic technique.
dateModified: 2021-11-07T16:34:37+03:00
---
Building a typographic scale might seem hard. Yet it's not all that difficult, as long as you learn some basic techniques and principles.
The first steps are to pick a **font family**, a starting value for the **font size** and a ratio which will serve as the **scaling factor**. Common values for these variables are the Roboto font family, a great choice due to its many different font weights, a starting font size of `18px` and a ratio of `1.618`, which is the golden ratio.
Based on these values, the basis of our typographic scale is an element with `font-size: 18px` and `line-height: 1.618`. Notice that the ratio is also applied to the line height. This allows the text to breathe a little more and makes it easier to read, while creating a consistent vertical rhythm.
Next, we are going to apply our ratio to **scale the font up or down**, as necessary. For example, we can multiply once by `1.618` for a sub heading, twice for a normal heading and three times for a large heading (e.g. our website's name on the home page). Similarly, we can scale the font down by dividing by our ratio to make elements less visually important (e.g. footnotes).
While this gives us a pretty solid setup, some elements might not look properly emphasized. To deal with this, we can use **font weight** to increase or decrease the visual importance of some elements. We could, for example, make headings bolder to make them more important. We could also make footnotes slightly bold to emphasize them as their font size is very small and they might get overlooked.
Putting it all together, we should end up with a typographic scale that looks similar to this:
![Typographic scale example](./illustrations/typography-example.png)
```css
:root {
--font-family: 'Roboto', sans-serif;
--font-size: 18px;
--font-weight: 300;
--line-height: 1.618;
}
* {
font-family: var(--font-family);
font-size: var(--font-size);
font-weight: var(--font-weight);
line-height: var(--line-height);
}
.sub-heading {
--font-size: 1.618rem;
--font-weight: 600;
}
.heading {
--font-size: 2.618rem;
--font-weight: 400;
}
.large-heading {
--font-size: 4.236rem;
--font-weight: 500;
}
.footnote {
--font-size: 0.618rem;
--font-weight: 500;
}
```
The above example is a very simple starting point to build your own typographic scale, applying some simple principles to create a visual hierarchy that feels natural and works well. Feel free to tweak the starting values of the variables and experiment to get the best result for your designs.