Reword some articles
This commit is contained in:
@ -6,7 +6,7 @@ authors: maciv,chalarangelo
|
||||
cover: blog_images/walking-on-top.jpg
|
||||
excerpt: Writing short, efficient Python code is not always straightforward. Read how we optimize our list snippets to increase performance using a couple of simple tricks.
|
||||
firstSeen: 2020-03-15T12:50:05+02:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 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.
|
||||
@ -18,7 +18,7 @@ def difference(a, b):
|
||||
return [item for item in a if item not in b]
|
||||
```
|
||||
|
||||
The above implementation may work well enough, but doesn't account for duplicates in `b`, making 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:
|
||||
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):
|
||||
@ -50,7 +50,7 @@ def difference(a, b):
|
||||
return [item for item in a if item not in _b]
|
||||
```
|
||||
|
||||
Another option worth mentioning when analyzing performance for this snippet is the use of a list comprehension versus using something like `filter()` and `list()`. Implementing the same code using the latter option would result in something like this:
|
||||
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):
|
||||
@ -58,6 +58,6 @@ def difference(a, 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, as it's 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.
|
||||
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).
|
||||
|
||||
@ -6,7 +6,7 @@ authors: chalarangelo
|
||||
cover: blog_images/orange-flower.jpg
|
||||
excerpt: Learn how to use CSS pseudo-classes to style an element based on changes to its state.
|
||||
firstSeen: 2020-08-18T19:56:12+03:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
CSS pseudo-classes provide a way to style elements, based on changes to their state. For example, `:hover` can be used to apply additional styles to an element when the user's pointer hovers over it.
|
||||
@ -15,7 +15,7 @@ Pseudo-classes let you apply styles to elements in relation to the content of th
|
||||
|
||||
### Commonly used pseudo-classes
|
||||
|
||||
Below is a list of the top 5 most commonly used pseudo-classes and their usage. This list is by no means complete; you should always refer to relevant documentation from authoritative sources, such as [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes) for more information.
|
||||
Below is a list of the top 5 most commonly used pseudo-classes and their usage. This list is by no means complete. You should always refer to relevant documentation from authoritative sources, such as [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes) for more information.
|
||||
|
||||
- `:hover`, `:focus` and `:active` are used to provide feedback for user interaction (e.g. changing a button's color on hover)
|
||||
- `:link` and `:visited` are useful for styling links based on navigation history (e.g. changing the color of visited links)
|
||||
|
||||
@ -6,14 +6,14 @@ authors: chalarangelo
|
||||
cover: blog_images/rock-climbing.jpg
|
||||
excerpt: Learn everything you need to know about higher-order functions with this short guide and level up your programming skills.
|
||||
firstSeen: 2020-09-24T12:54:08+03:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
Higher-order functions are functions that operate on other functions, either by taking them as arguments or by returning them as their results. This allows us to create an abstraction layer over actions, not just values.
|
||||
|
||||
The reason we can write higher-order functions in JavaScript is due to the fact that functions are values, meaning they can be assigned to variables and passed as values. You might also often hear the term _callback_ when referring to a function that is passed as an argument, due to it being called by the higher-order function. This is particularly common in JavaScript, with event handling, asynchronous code and array operations relying heavily on the concept of callbacks.
|
||||
The reason we can write higher-order functions in JavaScript is due to the fact that functions are values. This means they can be assigned to variables and passed as values. You might also often hear the term _callback_ when referring to a function that is passed as an argument. This is due to it being called by the higher-order function. Callbacks are particularly common in JavaScript, with event handling, asynchronous code and array operations relying heavily on them.
|
||||
|
||||
The main advantages of this technique are, as mentioned before, the abstraction layers it allows us to create as well as composition and more reusable and readable code. Most of the 30 seconds of code snippets are based on the idea of higher-order functions, as they promote small, easily digestible functions that can be easily composed to create more complex logic and are highly reusable.
|
||||
The main advantages of this technique are abstraction, composition, code reusability and readability. Most of the 30 seconds of code snippets are built with higher-order functions in mind. They are small, easily digestible functions that are highly reusable and can be composed to create more complex logic.
|
||||
|
||||
That being said, we can take a look at an example, utilizing some very simple functions:
|
||||
|
||||
@ -27,4 +27,4 @@ const evenValues = data.filter(isEven); // [2, 4, 6]
|
||||
const evenSum = data.filter(isEven).reduce(add); // 12
|
||||
```
|
||||
|
||||
In the above example, we define two simple functions that we then use as callbacks in `Array.prototype.reduce()` and `Array.prototype.filter()` to get the result we want. Both of these functions are higher-order functions, allowing us to create an abstraction layer for any action we might want to perform without having to rewrite how the filtering or reduction algorithm is to be applied every single time.
|
||||
In this example, we define two simple functions that we then use as callbacks in `Array.prototype.reduce()` and `Array.prototype.filter()` to get the result we want. Both of these functions are higher-order functions. This allows us to create an abstraction layer for any action we might want to perform without having to rewrite how the filtering or reduction algorithm is to be applied every single time.
|
||||
|
||||
@ -6,14 +6,14 @@ authors: chalarangelo
|
||||
cover: blog_images/cherry-trees.jpg
|
||||
excerpt: Learn different ways to memoize function calls in JavaScript as well as when to use memoization to get the best performance results.
|
||||
firstSeen: 2020-02-27T16:23:25+02:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
Memoization is a commonly used technique that you can use to speed up your code significantly. It uses a cache to store results, so that subsequent calls of time-consuming functions do not perform the same work another time. Based on this definition, we can easily extract some criteria that can help us decide when to use memoization in our code:
|
||||
Memoization is a commonly used technique that can help speed up your code significantly. This technique relies on a cache to store results for previously completed units of work. The purpose of the cache is to avoid performing the same work more than once, speeding up subsequent calls of time-consuming functions. Based on this definition, we can easily extract some criteria that can help us decide when to use memoization in our code:
|
||||
|
||||
- Memoization should be mainly used to speed up slow-performing, costly or time-consuming function calls
|
||||
- Memoization speeds up subsequent calls, so it is best used when you anticipate multiple calls of the same function under the same circumstances
|
||||
- Memoization stores results in memory, therefore it should be avoided when the same function is called multiple times under very different circumstances
|
||||
- Memoization is useful mainly in speeding up slow-performing, costly or time-consuming function calls
|
||||
- Memoization speeds up subsequent calls, so it's best used when you anticipate multiple calls of the same function under the same circumstances
|
||||
- Memoization stores results in memory, so it should be avoided when the same function is called multiple times under very different circumstances
|
||||
|
||||
A simple, object-oriented example of implementing memoization could be as follows:
|
||||
|
||||
@ -43,7 +43,7 @@ for (let i = 0; i < 100; i ++)
|
||||
myObject.firstNonEmptyItemMemo(); // ~70ms
|
||||
```
|
||||
|
||||
The above example showcases a way to implement memoization inside a class, however it makes the assumptions that the `data` structure will not be altered over the lifecycle of the object and that this is the only expensive function call we will make, so it cannot be reused. It also doesn't account for arguments being passed to the function, which would alter the result. A functional approach that would work with any given function and also account for arguments can be found in the form of the [memoize snippet](/js/s/memoize/), which uses a `Map` to store different values.
|
||||
This example showcases a way to implement memoization inside a class. However, it makes a couple of assumptions. First, it's assumed that the `data` structure will not be altered over the lifecycle of the object. Seconds, it's assumed that this is the only expensive function call we will make, so the code is not reusable. The example also doesn't account for arguments being passed to the function, which would alter the result. A functional approach would work with any given function and also account for arguments. Such an approach can be seen in the form of the [memoize snippet](/js/s/memoize/), which uses a `Map` to store different values.
|
||||
|
||||
We still recommend using that snippet as the primary way to memoize a function, however JavaScript's [Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) provides an interesting alternative via the use of the `handler.apply()` trap, which can be used for this purpose as follows:
|
||||
|
||||
|
||||
@ -6,12 +6,12 @@ authors: chalarangelo
|
||||
cover: blog_images/rocky-lake.jpg
|
||||
excerpt: JavaScript's `switch` statement often feels hard to remember and a little bit out of place. Maybe it's time to use object literals, instead.
|
||||
firstSeen: 2021-04-01T12:00:00+03:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
JavaScript's `switch` statement is one of the few things I find hard to remember the syntax for (so glad VS Code has autocomplete). It also feels a little bit out of place syntactically, as it's the only thing that doesn't use curly braces and you need to remember to `break` for every `case`. Moreover, its performance is less than stellar as its control flow is procedural.
|
||||
|
||||
Luckily, JavaScript provides another alternative for most use-cases I can think of for `switch` statements - object literals. The idea is to define an object with a key for each `case` you would have in a `switch` statement, then access its value directly using the expression you would pass to the `switch` statement.
|
||||
Luckily, JavaScript's object literals are a pretty good alternative for most `switch` statement use-cases I can think of. The idea is to define an object with a key for each `case` you would have in a `switch` statement. Then you can access its value directly using the expression you would pass to the `switch` statement.
|
||||
|
||||
```js
|
||||
let fruit = 'oranges';
|
||||
@ -87,7 +87,7 @@ const logFruit = {
|
||||
(logFruit[fruit] || logFruit['default'])(); // Logs: 'Known fruit'
|
||||
```
|
||||
|
||||
To wrap this all up, we can generalize and extract this logic into a simple reusable function, which we will supply with the lookup object and an optional name for the default case (we'll default to `_default` to avoid any conflicts). This function will in turn return a function with the appropriate lookup logic and we can use it to replace any `switch` statement.
|
||||
To wrap this all up, we can generalize and extract this logic into a simple reusable function. We will supply it with the lookup object and an optional name for the default case (we'll default to `_default` to avoid any conflicts). This function will in turn return a function with the appropriate lookup logic and we can use it to replace any `switch` statement.
|
||||
|
||||
```js
|
||||
const switchFn = (lookupObject, defaultCase = '_default') =>
|
||||
|
||||
@ -6,12 +6,12 @@ authors: maciv
|
||||
cover: blog_images/leaves-read.jpg
|
||||
excerpt: Learn 3 easy ways to swap the values of two variables in Python.
|
||||
firstSeen: 2021-02-04T11:00:00+02:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
### Using a temporary variable
|
||||
|
||||
The simplest way to swap the values of two variables is using a `temp` variable. The `temp` variables is used to store the value of the fist variable (`temp = a`), allowing you to swap the value of the two variables (`a = b`) and then assign the value of `temp` to the second variable.
|
||||
The simplest way to swap the values of two variables is using a `temp` variable. The `temp` variables is used to store the value of the fist variable (`temp = a`). This allows you to swap the value of the two variables (`a = b`) and then assign the value of `temp` to the second variable.
|
||||
|
||||
```py
|
||||
a = 11
|
||||
|
||||
@ -6,12 +6,12 @@ authors: maciv
|
||||
cover: blog_images/succulent-red-light.jpg
|
||||
excerpt: When developing React components, you might often need to conditionally apply a className. Learn how to handle empty classNames correctly using this handy tip.
|
||||
firstSeen: 2020-11-06T20:17:21+02:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
When developing React components, you often need to conditionally apply a `className` attribute to one or more elements. Sometimes, you will have two or more possible values depending on a condition, but there are also times that you might apply a `className` based on a condition or leave it completely empty otherwise.
|
||||
When developing React components, you often need to conditionally apply a `className` attribute to one or more elements. Sometimes, you will have two or more possible values depending on a condition. But there are also times that you might apply a `className` based on a condition or leave it completely empty otherwise.
|
||||
|
||||
However, there is a correct way to handle a conditional empty `className` and an incorrect one and there are many examples of handling this incorrectly all around the web. Consider the following example:
|
||||
There is a correct way to handle a conditional empty className and an incorrect one. Surprisingly, the incorrect way is pretty common and examples of it can be found all around the web. Consider the following code:
|
||||
|
||||
```jsx
|
||||
const MyComponent = ({ enabled }) => {
|
||||
@ -31,8 +31,8 @@ ReactDOM.render(
|
||||
);
|
||||
```
|
||||
|
||||
In the code example above, we create two very similar components, both of which conditionally set the `className` of an element based on the value of the `enabled` prop. The first one will set the `className` to an empty string if `enabled` is `false` and the second one will set it to `null`.
|
||||
In this code example, we define two very similar components. Both of them conditionally set the `className` of an element based on the value of the `enabled` prop. The first one will set the `className` to an empty string if `enabled` is `false` and the second one will set it to `null`.
|
||||
|
||||
Both will result in a very similar output, however, if you carefully inspect the HTML, you will notice that the first one will render `<div class>Hi</div>` whereas the second one will render `<div>Hi</div>`. This kind of markup (an attribute being present but without value) is rather uncommon and you'd rarely ever see something like that without React. This subtle difference is quite important and might be the root of a lot of problems, especially when trying to select elements with/without any classes using CSS selectors (e.g. `[class]`/`:not([class])`).
|
||||
The resulting output is pretty similar. However, if you carefully inspect the HTML, you will notice that the first one will render `<div class>Hi</div>` whereas the second one will render `<div>Hi</div>`. This kind of markup (an attribute being present but without value) is rather uncommon and you'd rarely ever see something like that without React. This subtle difference is quite important and might be the root of a lot of problems, especially when writing CSS selectors for elements with/without any classes (e.g. `[class]`/`:not([class])`).
|
||||
|
||||
Therefore, you should prefer `null` when you don't want to add a `className` to an element, instead of an empty string. It keeps the markup cleaner and might help prevent some potential issues.
|
||||
|
||||
@ -6,7 +6,7 @@ authors: chalarangelo
|
||||
cover: blog_images/bunny-poster.jpg
|
||||
excerpt: When working with multiple `useState` hooks in React, things can get a bit complicated while debugging. Luckily, there's an easy way to label these values.
|
||||
firstSeen: 2021-05-06T12:00:00+03:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
When working with multiple `useState` hooks in React, things can get a bit complicated while debugging. Luckily, there's an easy way to label these values, using the [`useDebugValue`](https://reactjs.org/docs/hooks-reference.html#usedebugvalue) hook to create a custom `useStateWithLabel` hook:
|
||||
@ -30,4 +30,4 @@ ReactDOM.render(<Counter />, document.getElementById('root'));
|
||||
// StateWithLabel: "counter: 0"
|
||||
```
|
||||
|
||||
This hook is obviously meant mainly for development, but it can also be useful when creating React component or hook libraries. Additionally, you can easily abstract it in a way that the label is ignored in production builds (i.e. by exporting a hook that defaults back to `useState` when building for a production environment).
|
||||
This hook is obviously meant mainly for development, but it can also be useful when creating React component or hook libraries. Additionally, you can easily abstract it in a way that the label is ignored in production builds. An example would be exporting a hook that defaults back to `useState` when building for a production environment.
|
||||
|
||||
@ -6,14 +6,14 @@ authors: maciv,chalarangelo
|
||||
cover: blog_images/colorful-lounge.jpg
|
||||
excerpt: Testing React components that update asynchronously with React Testing Library is a common scenario. Learn how to deal with common issues and speed up your testing.
|
||||
firstSeen: 2020-08-13T20:21:33+03:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
### Components that update asynchronously
|
||||
|
||||
Recently, while working on our latest side-project, [boardeaux](https://github.com/Trinityyi/boardeaux), 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.
|
||||
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 make sure 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.
|
||||
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:
|
||||
|
||||
@ -92,13 +92,13 @@ act(() => {
|
||||
This ensures that you're testing the behavior the user would see in the browser.
|
||||
```
|
||||
|
||||
This message, however, was not very helpful in identifying the underlying issue. The only thing it highlighted was that our test didn't update the component style immediately, but there were pending updates after the test completed. To put it plainly, our test was failing because the `dragStart` event didn't immediately update the `Card` components' style (i.e. set the new `opacity`).
|
||||
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, our `Card` component is connected to Redux, which might relate to the issue, but it would most likely happen even without Redux, probably due to the fact that `collect` takes some amount of time to run and send an update to the component.
|
||||
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()`, which 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.
|
||||
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:
|
||||
|
||||
@ -118,7 +118,7 @@ This required some searching, which eventually led us to [this issue](https://gi
|
||||
}
|
||||
```
|
||||
|
||||
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 our original testing code, except for the dependency bump and the script change described above. Here's our test after making the necessary changes:
|
||||
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';
|
||||
|
||||
@ -6,10 +6,10 @@ authors: chalarangelo
|
||||
cover: blog_images/sparkles.jpg
|
||||
excerpt: Testing Redux-connected components with React Testing Library is a very common scenario. Learn how to use this simple utility function to speed up your testing.
|
||||
firstSeen: 2020-07-15T13:54:26+03:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 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, especially when writing the boilerplate to connect to your redux store.
|
||||
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:
|
||||
|
||||
@ -38,7 +38,7 @@ const renderConnected = (
|
||||
export default renderConnected;
|
||||
```
|
||||
|
||||
The utility above uses the `createStore` function and the `<Provider>` component to wrap a redux-connected component to the passed state, while using React Testing Library's `render` to finally render the result.
|
||||
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:
|
||||
|
||||
|
||||
@ -6,18 +6,18 @@ authors: chalarangelo
|
||||
cover: blog_images/typography.jpg
|
||||
excerpt: Typography might seem intimidating, but you can quickly and easily create a simple typographic scale with this basic technique.
|
||||
firstSeen: 2020-08-18T19:27:17+03:00
|
||||
lastUpdated: 2021-06-12T19:30:41+03:00
|
||||
lastUpdated: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
Building a typographic scale, while seemingly hard, is not all that difficult, as long as you learn some basic techniques and principles.
|
||||
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, which is a great choice as it has many different font weights, a starting font size of `18px` and a ratio of `1.618`, which is the golden ratio.
|
||||
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, which allows the text to breathe a little more and makes it easier to read, while creating a consistent vertical rhythm.
|
||||
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, but we could also make footnotes slightly bold to emphasize them as their font size is very small and they might get overlooked.
|
||||
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:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user