Technical Documentation: Chalarangelo/30-seconds-of-code
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
30 seconds of code
Coding articles to level up your development skills
- Visit the website to view all articles.
- Search for articles and collections that suit your needs, using the name, tags, language or description.
- Browse all articles or individual collections for each topic.
- Click on each card to view the whole article, including code snippets, explanation and examples.
- If you like the project, give it a star. It means a lot.
Want to contribute?
- New content contributions are not accepted at this time.
- You can, however, report problems with the website or suggest improvements by opening an issue.
Credits
- This repository is maintained by Angelos Chalaris.
- All code snippets are licensed under the CC-BY-4.0 License, unless explicitly stated otherwise.
- Any other material (including text content, images, the website source code, logos, names and trademarks) are not to be used without the explicit consent of the owner.
- The website is powered by Netlify, Astro & GitHub.
- All of the photography is provided by Unsplash.
Snippets Js S Proxy Array Negative Index (`content/snippets/js/s/proxy-array-negative-index.md`)
title: Negative index in JavaScript array shortTitle: Negative index array proxy language: javascript tags: [proxy,array] cover: budapest-palace excerpt: Ever wanted to use negative indices in JavaScript arrays? Here's a simple way to do it using a Proxy. listed: true dateModified: 2022-10-02
Array.prototype.slice() provides an easy way to access elements from the end of an array, using a negative start value. While this sounds convenient, the resulting value is an array, so it's necessary to use an index to get an individual element.
This is usually not too bad, but it's interesting to explore other options to understand the language better. In this case, we can use a Proxy object to allow accessing data in an array using negative indexes. To do so, an appropriate handler needs to be defined for the get trap.
The trap's second argument corresponds to the passed index, however it's a string, so it must first be converted to a number using Number(). Then, Array.prototype.length can be used to calculate the position of the actual element. Finally, Reflect.get() can be used to get the value at the specific index, but expects its second argument to be a string.
Putting everything together, this is what this looks like:
const handler = {
get(target, key, receiver) {
const index = Number(key);
const prop = index < 0 ? `${target.length + index}` : key;
return Reflect.get(target, prop, receiver);
},
};
const createArray = (...elements) => {
const arr = [...elements];
return new Proxy(arr, handler);
};
let arr = createArray('a', 'b', 'c');
arr[-1]; // 'c'
arr[-1]; // 'b'[!IMPORTANT]
As of May, 2025,
Array.prototype.at()is widely available in all modern browsers and Node.js. You should probably consider using it, if possible, as it provides a more straightforward and performant interface for accessing elements in an array using negative indices without the need for a custom solution.
Snippets Js S Test Driven Development Intro (`content/snippets/js/s/test-driven-development-intro.md`)
title: Kickstart a JavaScript project with Test-Driven Development shortTitle: Test-Driven Development language: javascript tags: [webdev,testing] cover: succulent-5 excerpt: I recently built a project using Test-Driven Development (TDD) and it was a great experience. Here's why I think you should try it, too. listed: true dateModified: 2025-03-17 journeyId: js/tdd-library-design
I recently decided to try out Test-Driven Development (TDD) for a small project. While I've had some experience with the methodology before, I've never tried bootstrapping a project with it from the start.
What is TDD?
Test-Driven Development, often referred to as TDD, is a software development methodology that relies on writing tests before writing the actual code. The idea is to define the desired behavior of the code through tests and then write the code to achieve that behavior. This approach transforms the traditional development process, by making testing an integral part of the coding cycle rather than an afterthought.
In short, in TDD, we test first, code later. This is in contrast to the traditional approach of coding first and testing later, or not at all.
Tooling
I decided to use Vitest for this project. It's the tool I've settled on, after trying the likes of Jest and Mocha. It feels faster and, in my experience, it's easier to set up and configure than its counterparts.
[!TIP]
I didn't really have to configure anything for this project, as I was using native ES modules and, later in the development cycle, Vite, which works great with Vitest out of the box.
As far as syntax is concerned, there's nothing particularly special about Vitest. In fact, it's practically the same as every other testing library, using the classic trinity of describe, test (aliased conveniently as it), and expect for assertions.
import { describe, it, expect } from 'vitest';
import { myFunction } from '../src/myFunction.js';
describe('myFunction', () => {
it('should return true when called', () => {
expect(myFunction()).toBe(true);
});
});It also comes with a lot of very convenient matchers, mocking and pretty much everything you'd expect from a modern testing library.
import { describe, it, expect } from 'vitest';
import { fetchUser } from '../src/fetchUser.js';
describe('fetchUser', () => {
it('should return a user object', () => {
const user = fetchUser('john_doe');
expect(user).toMatchObject({ username: 'john_doe' });
expect(user.age).toBeGreaterThan(18);
expect(user).toHaveProperty('email');
expect(user.name).not.toBeUndefined();
});
});I especially like the test.each() and describe.each() functions, which allow you to run the same test with different parameters, sparing you the hassle of repeating the same assertions over and over again.
import { describe, it, expect } from 'vitest';
import { isNil } from '../src/isNil.js';
describe('isNil', () => {
it.each(
[null, undefined]
)('should return true %s', (val) => {
expect(isNil(val)).toBe(true)
});
it.each(
[false, '', 0, {}, [], () => {}]
)('should return false with %s', val => {
expect(isNil(val)).toBe(false);
});
});Custom matchers
One thing I want to mention is how easy it was to roll up my own custom matcher. As my project is centered around regular expressions, I wanted a matcher that would easily check if an expression matches a given string.
Surprisingly, this only took me about 10 lines of code:
import { expect } from 'vitest';
expect.extend({
toMatchString(regex, string) {
const { isNot } = this;
const pass = regex.test(string);
return {
pass,
message: () =>
`expected ${regex}${isNot ? ' not' : ''} to match "${string}"`,
};
},
});The entire process is as simple as calling expect.extend() and defining the behavior. To use it in combination with not, I only had to check the isNot property of the context object. Now I can use it in my tests like this:
import { describe, it, expect } from 'vitest';
import './matchers.js';
import { hexDigit } from '../src/hexDigit.js';
describe('hexDigit', () => {
it('should match hex digit', () => {
expect(hexDigit).toMatchString('0');
expect(hexDigit).toMatchString('9');
expect(hexDigit).toMatchString('a');
expect(hexDigit).toMatchString('f');
expect(hexDigit).not.toMatchString('g');
expect(hexDigit).not.toMatchString('-');
});
});[!NOTE]
There's a caveat to my unbridled enthusiasm for custom matchers and that is I haven't used them all that much just yet. Yet, they seem like a very easy, elegant and hassle-free solution in my limited experience.
Why TDD?
At this point, you might be starting to wonder why I chose TDD for this project. The truth is, I sort of already knew what I was going to make, but I had a few concerns about various aspects of the project and I thought that TDD could help me with them.
Better API design
I'll dive deeper into this point in a future article, but one of the key concerns I was facing was the developer experience. Designing a user-friendly library API is no small task, one I've botched in the past.
By writing tests first, I was able to decide on how I want the library to behave, what methods to expose and what sorts of arguments they should take. I was also able to focus on using the library before implementing it. This naturally makes it easier to spot the rough edges early on and make the API easier to use.
import { describe, it, expect } from 'vitest';
import { myFunction } from '../src/myFunction.js';
// Wait, what's that second `true` again? Maybe redesign this.
describe('myFunction', () => {
it('should do something', () => {
const result = myFunction(10, true, false, true);
expect(result).toBe('expected result');
});
});It also puts you in the shoes of the user, instead of the library author – a perspective that can be hard to maintain otherwise. This means that you're evaluating the tool from the perspective of usability, instead of the traditional viewpoint of minimizing complexity when writing the code or working around limitations and building clever abstractions.
Higher confidence
One of the things I noticed as I went through the process was that I was pretty confident about my code. I knew that if I broke something, the tests would catch it. This is a great feeling to have, especially when you're working on a project that you're not entirely sure about or that hasn't materialized fully yet.
import { describe, it, expect } from 'vitest';
import { myFunction } from '../src/myFunction.js';
// I'm only done when all these tests pass
describe('myFunction', () => {
it('should do this', () => {
// ...
});
it('should also do that', () => {
// ..
});
it('should absolutely not do this', () => {
// ...
});
});The magic of TDD lies in exactly that. When a function, a module, anything is completed, it lets you know. All relevant tests pass, the behavior is as expected, and you can move on to the next thing. I was especially keen on this aspect, as I could only work on this in small bursts and I wanted to make sure that I was making progress every time I sat down to work on it.
Easier refactoring
As I'll also explore in a future piece, I refactored the library a handful of times, changing the API in some cases. By refactoring the tests to match the result I was looking for, I was always able to know when the refactor was complete and functional. Without tests to back me up, I would have been much more hesitant to make some of these changes.
import { describe, it, expect } from 'vitest';
import { myFunction } from '../src/myFunction.js';
// I've refactored this test to how I like the API, now let's fix the code
describe('myFunction', () => {
it('should do something', () => {
const result =
myFunction(10, { test: true, value: false, repeat: true });
expect(result).toBe('expected result');
});
});This was especially true when one small change in a file broke the entire library at its core and I wouldn't have realized, because it was a pretty subtle mistake relating to spreading arguments in a function call. Ouch!
Takeaways
I've been skeptical of TDD for years now, but there's nothing like true hands-on experience to shape one's opinion. Having heard of the benefits before, I can tell you that they're as described on the tin.
Would I use it again? Absolutely. In fact, I think that all my smaller weekend-turned-month projects would benefit from this methodology. It also seems like it forced me down a path of producing code focused on entirely different aspects than I usually do.
Is it a silver bullet? No. I can see how it would be difficult to implement in larger projects, especially ones maintained by large teams. I think one of the main shortcomings is that it's hard to retrofit TDD into an existing codebase and, when there are too many moving parts, it can be hard to keep track of everything. But for new projects, especially smaller ones, I think it's a great way to write code.
Conclusion
In conclusion, I found that TDD was a great way to write code. It helped me design what I wanted the library to do first, outline use-cases and the API, and gave me confidence in my code. It also made refactoring easier, as I could be sure that I hadn't broken anything.
At the end of the day, I give a solid recommendation for using TDD for smaller projects, especially when you're just starting out with a new idea or library. It's a great way to write code and it's a great way to make sure that your code is working as expected.
Snippets Js S Tf Idf Inverted Index (`content/snippets/js/s/tf-idf-inverted-index.md`)
title: Implementing search in JavaScript, using TF-IDF and an inverted index shortTitle: TF-IDF and inverted index search language: javascript tags: [algorithm,string,regexp] cover: typewriter excerpt: Building on top of the Porter stemmer, we'll explore how to use TF-IDF and an inverted index to implement a search algorithm in JavaScript. listed: true dateModified: 2025-04-09 journeyId: js/search
In the previous article, I went into detail how one could implement the Porter stemming algorithm in JavaScript. While the algorithm can be used along with a simple tokenization strategy to search for keywords in a document, it's insufficient for complex searching tasks. In this article, we'll explore how we can use TF-IDF for more advanced search tasks and implement an inverted index to speed up the search process.
Tokenization
Before we begin, let's establish a simple tokenization strategy. As we're only concerned with plaintext documents, we can use a regular expression to split the text into words.
const tokenize = str =>
str
.split(/[^a-z0-9\-']+/i)
.filter(tkn => !tkn.length < 2)
.map(tkn => tkn.toLowerCase());
const text = 'The quick brown fox jumps over the lazy dog.';
const tokens = tokenize(text);
// ['the', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog']All we're doing here is matching characters other than alphanumeric characters, hyphens, and apostrophes and splitting the text into words. We're also filtering out short words and converting all words to lowercase for easier comparison.
Stopword removal
Stopwords are common words that are often filtered out before or after processing of natural language data. These words are usually not relevant for search tasks and can be safely removed. Lists of stopwords are readily available online, but for simplicity, we'll use a small list of common English stopwords.
const removeStopwords = (stopWords, words) =>
words.filter(tkn => !stopWords.includes(tkn));
const commonStopWords = [
'a', 'am', 'an', 'are', 'be', 'been', 'being', 'by', 'cannot', 'could',
'did', 'do', 'does', 'doing', 'for', 'had', 'has', 'have', 'having', 'he',
'her', 'hers', 'him','his', 'i', 'if', 'is', 'it', 'its', 'me', 'my', 'no',
'nor', 'of', 'or', 'other', 'our', 'ours', 'she', 'should', 'so', 'such',
'that', 'the', 'their', 'theirs', 'them', 'then', 'there', 'these', 'they',
'those', 'too', 'was', 'we', 'were', 'who', 'whom', 'with', 'would', 'you',
'your', 'yours'
];
const text = 'The quick brown fox jumps over the lazy dog.';
const tokens = tokenize(text);
const filteredTokens = removeStopwords(commonStopWords, tokens);
// ['quick', 'brown', 'fox', 'jumps', 'over', 'lazy', 'dog']Arbitrary search
Having a basic understanding of these steps, we can use the Porter stemmer to stem the words and perform a search. We'll use the Porter stemmer from the previous article and search for the word 'jump'.
// Assuming the `porterStemmer` function is available
import { porterStemmer } from './porterStemmer.js';
const documents = [];
const parseDocument = document => {
const tokens = tokenize(document);
const filteredTokens = removeStopwords(commonStopWords, tokens);
const stemmedTokens = filteredTokens.map(porterStemmer);
return stemmedTokens;
};
const addDocument = document => {
const terms = parseDocument(document);
documents.push(terms);
};
const search = query => {
const stemmedQuery = porterStemmer(query);
const results = documents.filter(doc => doc.includes(stemmedQuery));
return results;
};
addDocument(
'JavaScript is a programming language, used in programming the web.'
);
addDocument(
'Python is a high-level programming language, used in data science.'
);
addDocument(
'CSS is a style sheet language, used in web development.'
);
addDocument(
'HTML is a markup language, used in web development.'
);
search('javascript'); // Returns the first document [0]
search('language'); // Returns all documents [0, 1, 2, 3]
search('programming language'); // Returns nothing []
search('web'); // Returns the first, third, and fourth document [0, 2, 3]As you can see, using this technique has some limitations. While we can extend it to search for multiple words, it's overall not particularly good at ranking the results. This is why we'll be exploring a better measure of relevance, TF-IDF.
TF-IDF
TF-IDF refers to the term frequency-inverse document frequency. It is a measurement that is intended to reflect how important a word is to a document in a collection or corpus. The TF-IDF value increases proportionally to the number of times a word appears in the document and is offset by the frequency of the word in the corpus.
The implementation is straightforward. All we need to add is a Map to use as a vocabulary and add the terms found in each document to it. Then, we can calculate the term frequency (TF) of a term in a document by dividing the number of times the term appears in the document by the total number of terms in the document.
Similarly, we can calculate the inverse document frequency (IDF) of a term by dividing the total number of documents by the number of documents that contain it. Finally, we can calculate the TF-IDF of a term in a document by multiplying the two values.
[!NOTE]
I'm using the IDF formula with smoothing in these examples, but you can switch it out for a different formula, if you prefer.
We can then search for documents by tokenizing the given query, then calculating the TF-IDF of each term in each document and summing the values. The documents can then be sorted by the sum of the TF-IDF values to get the most relevant documents.
const documents = [];
const vocabulary = new Map();
const addDocument = document => {
const terms = parseDocument(document);
documents.push(terms);
terms.forEach(term => {
vocabulary.set(term, (vocabulary.get(term) || 0) + 1);
});
};
const calculateTF = (term, documentIndex) => {
const docTerms = documents[documentIndex];
const termCount = docTerms.filter((t) => t === term).length;
return termCount / docTerms.length;
};
const calculateIDF = term => {
const totalDocs = documents.length;
const docsWithTerm = vocabulary.get(term) || 0;
return Math.log((totalDocs + 1) / (docsWithTerm + 1)) + 1;
};
const calculateTFIDF = (term, documentIndex) =>
calculateTF(term, documentIndex) * calculateIDF(term);
const search = query => {
const queryTerms = parseDocument(query);
return documents
.reduce((scores, doc, index) => {
const score = queryTerms.reduce(
(score, term) => (score += calculateTFIDF(term, index)), 0
);
scores.push({ document: index, score });
return scores;
}, [])
.filter(result => result.score > 0)
.sort((a, b) => b.score - a.score);
};
// Supposing the documents are the same as before
search('javascript'); // Returns the first document [1]
search('language'); // Returns all documents [0, 3, 1, 2]
search('programming language'); // Returns all documents [0, 3, 1, 2]
search('web'); // Returns the first, third, and fourth document [0, 3, 2]While subtle, in this example you can see the last query returning the documents in a different order. This is a result of ranking the documents by their TF-IDF values, ranking the documents by relevance.
Inverted index
The TF-IDF implementation is already a significant improvement over the previous search method. However, it can still be slow for large collections of documents. To speed up the search process, we can use an inverted index.
An inverted index is a data structure that maps terms to the documents that contain them. This allows us to quickly find documents that contain a given term. We can use a Map to store the inverted index, where the keys are terms and the values are maps that map document IDs to term frequencies.
Then, we can alter our search function to use the inverted index to calculate the TF-IDF values. This way, we can avoid iterating over all documents for each term in the query, significantly speeding up the search process.
const documents = [];
const invertedIndex = new Map();
const addDocument = document => {
const terms = parseDocument(document);
documents.push(terms);
terms.forEach(term => {
if (!invertedIndex.has(term)) invertedIndex.set(term, new Map());
const docMap = invertedIndex.get(term);
docMap.set(documents.length - 1, terms.filter((t) => t === term).length);
});
};
const search = query => {
const queryTerms = parseDocument(query);
const scores = documents.reduce((scores, doc, docId) => {
const score = queryTerms.reduce((score, term) => {
if (invertedIndex.has(term)) {
const indexEntry = invertedIndex.get(term).get(docId);
if (!indexEntry) return score;
const tf = indexEntry / doc.length;
const idf =
Math.log(
(documents.length + 1) / (invertedIndex.get(term).size + 1)
) + 1;
return score + tf * idf;
}
}, 0);
if (score > 0) scores.set(docId, score);
return scores;
}, new Map());
const results = Array.from(scores.entries())
.sort((a, b) => b[1] - a[1])
.map(([id, score]) => ({ id, score }));
return results;
};
// Supposing the documents are the same as before
search('javascript'); // Returns the first document [0]
search('language'); // Returns all documents [0, 3, 1, 2]
search('programming language'); // Returns all documents [0, 1, 3, 2]
search('web'); // Returns the first, third, and fourth document [0, 3, 2]Conclusion
That's all for the time being. We've stitched together all the pieces of a simple, yet powerful and performant search algorithm. It's definitely not as powerful as a full-fledged search engine, but it's a good starting point for understanding how search works under the hood and get into natural language processing (NLP).
Snippets Python S Min Max Element Index (`content/snippets/python/s/min-max-element-index.md`)
title: Index of min or max element language: python tags: [math,list] cover: two-cities excerpt: Find the index of the element with the minimum or maximum value in a list. listed: false dateModified: 2024-07-18
To find the index of the element with the minimum or maximum value in a list, you can use the min() and max() functions to get the minimum or maximum value in the list, and then use the list.index() method to find the index of that value.
def min_element_index(arr):
return arr.index(min(arr))
def max_element_index(arr):
return arr.index(max(arr))
min_element_index([3, 5, 2, 6, 10, 7, 9]) # 2
max_element_index([3, 5, 2, 6, 10, 7, 9]) # 4Snippets Articles Snippet Template (`content/snippets/articles/snippet-template.md`)
title: My amazing story shortTitle: Amazing story language: javascript tags: [webdev] cover: image excerpt: A short summary of your story up to 140 characters long. listed: true dateModified: 2021-06-13
Write your story here.
Snippets Css Snippet Template (`content/snippets/css/snippet-template.md`)
title: My amazing story shortTitle: Amazing story language: css tags: [layout] cover: image excerpt: A short summary of your story up to 140 characters long. listed: true dateModified: 2021-06-13
Write your story here.
Snippets Git Snippet Template (`content/snippets/git/snippet-template.md`)
title: My amazing story shortTitle: Amazing story language: git tags: [branch] cover: image excerpt: A short summary of your story up to 140 characters long. listed: true dateModified: 2021-06-13
Write your story here.
Snippets Html Snippet Template (`content/snippets/html/snippet-template.md`)
title: My amazing story shortTitle: Amazing story language: html tags: [link] cover: image excerpt: A short summary of your story up to 140 characters long. listed: true dateModified: 2021-06-13
Write your story here.
Snippets Js Snippet Template (`content/snippets/js/snippet-template.md`)
title: My amazing story shortTitle: Amazing story language: javascript tags: [array] cover: image excerpt: A short summary of your story up to 140 characters long. listed: true dateModified: 2021-06-13
Write your story here.
Snippets Python Snippet Template (`content/snippets/python/snippet-template.md`)
title: My amazing story shortTitle: Amazing story language: python tags: [list] cover: image excerpt: A short summary of your story up to 140 characters long. listed: true dateModified: 2021-06-13
Write your story here.
Snippets React Snippet Template (`content/snippets/react/snippet-template.md`)
title: My amazing story shortTitle: Amazing story language: react tags: [hooks] cover: image excerpt: A short summary of your story up to 140 characters long. listed: true dateModified: 2021-06-13
Write your story here.
Snippets Update Logs Snippet Template (`content/snippets/update-logs/snippet-template.md`)
title: My amazing update log shortTitle: Amazing update log tags: [updatelog] cover: image excerpt: A short summary of your update log up to 140 characters long. listed: false dateModified: 2024-12-11
Write your update log here.
Snippets Articles S 4 Seo Tips For Developers (`content/snippets/articles/s/4-seo-tips-for-developers.md`)
title: 4 SEO tips for developers shortTitle: SEO tips tags: [webdev,seo] cover: sunrise-over-city excerpt: SEO is a very relevant topics that most developers are inexperienced in. Here are 4 actionable SEO tips you can implement today. listed: true dateModified: 2021-06-12
SEO is becoming increasingly relevant as the internet keeps growing. While most web developers usually have little experience with SEO, it is not particularly difficult to handle some of the basics. Here are my top SEO tips for web developers:
Craft user-friendly URLs and map the appropriately
First and foremost, start at the very beginning, which is your URL slugs. For URL slugs the rule is that you want them to be human-readable, with words separated by dashes and with no random letters or digits added. Secondly, it's a good idea to make sure that parts of your URLs match to a logical structure in your website (e.g. /blog/posts/awesome-list-of-seo-tips). Finally, take the time to build a sitemap and redirect any broken or old URLs to new ones. This will help reduce 404 pages.
Use structured data to help Google understand your pages
Structured data is what Google uses to power its featured snippets, those little cards that appear at the top of certain searches. It's a good idea to set up structured data for your pages, which will help you rank higher in search results and possibly even land a featured snippet every once in a while. Just make sure to find the appropriate structured data type and include it in your page and you should be good to go.
Set up Google Analytics and Google Search Console
This is hardly a new tip, but I think it deserves a spot on the list, as both of these tools are extremely important. Google Analytics allows you to track user behavior and gather data that can help you identify problems and opportunities, while Google Search Console is helpful for figuring out what users are searching for before landing on your website.
Keep an eye on your markup, performance and accessibility
Last but not least, something that is probably already on your list, is optimizing your code. This includes everything from writing semantic markup and keeping requests to a minimum to optimizing for all device types, making your website accessible and ensuring fast load times. Keep in mind that websites have many moving parts that evolve and change over time, so it's a good idea to audit your website often with a tool like Lighthouse.
Snippets Articles S 7 Chrome Extensions For Web Developers (`content/snippets/articles/s/7-chrome-extensions-for-web-developers.md`)
title: 7 essential Chrome extensions for web developers shortTitle: Essential Chrome extensions tags: [webdev,devtools] cover: computer-screens excerpt: Boost your productivity with 7 must-have Google Chrome developer tool extensions. listed: false dateModified: 2021-06-12
Google Chrome's developer tools are nothing short of amazing, but there are a few missing tools that can increase your productivity even further. Here are my personal favorite extensions that I use everyday:
CSS Peeper
CSS Peeper is an all-in-one tool for CSS inspection that allows you to quickly peek at the styles, fonts, color palettes and assets of any website you want.
React Developer Tools
React Developer Tools is the de facto extension when working with React, providing all the necessary inspection tools for your React applications.
LastPass
LastPass keeps your accounts and API keys secure, by providing a free password manager complete with password generation and password-protected notes.
uBlock Origin
uBlock Origin is a simple yet effective ad blocker extension that you can just install and forget about, as it keeps your browsing experience ad-free.
VisBug
VisBug allows you to tweak website designs on the fly, using very intuitive controls and tools, without having to delve into any code at all.
JSON Viewer
JSON Viewer provides some much needed syntax highlighting and style customization to JSON responses, which are nowadays very commonplace in most web APIs.
EditThisCookie
EditThisCookie is a powerful cookie manager extension, that allows you to add, delete, edit and even export and import cookies quickly and efficiently.
Snippets Articles S 8 Tips For Remote Work (`content/snippets/articles/s/8-tips-for-remote-work.md`)
title: 8 tips for working from home tags: [webdev,career,programming,jobs] cover: kettle-laptop excerpt: Working from home seems great compared to going to the office, but there are challenges that come with it that you need to overcome. listed: false dateModified: 2021-06-12
Working from home (also known as remote work) seems like a great alternative to going to the office every morning, but it comes with its own set of challenges that you need to overcome in order to stay healthy and productive. Here are my tips for achieving just that:
Set up your work environment
Working from home has its perks, but nothing beats a well-designed office space where everything is set up with only one purpose in mind: working. Figure out your home office, experiment with different settings and understand what works best for you as soon as possible. An ideal working space is comfortable, quiet and has the right equipment for you.
Establish ground rules
Most likely you are not living alone, so you have to establish some ground rules with your roommate or significant other. It's up to you to drive the point home that during working hours you are, for the most part, not home. Sure, you can answer the door if you expect a delivery, but that's pretty much as far as you can go. People should not bother you during working hours, unless absolutely necessary, as small distractions pile up fast.
Inform others of your availability
It's important to let people know that you are online and working or that you are taking a short break for lunch. Remember that you are still part of a team that requires coordination and others probably depend on your work to some extent. Remember to update your status as necessary to make collaboration easier.
Socialize with coworkers
Working from home can lead to feelings of loneliness, disconnect, isolation which can quickly spin out of control and lead to depression. Communicate with people on your team as if you were in the same room. A healthy amount of communication will help you feel more like you are all working together rather than each one on their own.
- Be your best professional self
Nobody might be watching you at home, so you can theoretically slack off as much as you like in your pajamas, but that's not very professional. Try to dress appropriately in case you join a video call and behave professionally, so no inappropriate websites or hours upon hours of checking social media. Ask yourself if someone in a shared office space would do whatever it is you are doing and, if the answer is no, stop doing it.
Plan your daily and weekly tasks
Having a coherent working plan helps you organize your time and prioritize important tasks above trivial ones. It also helps to put things into perspective and have a general idea of what other people on the team are working on. Plan ahead of time together with your team and keep each other posted on the progress of each task. Short term plans help you get through the day, long term plans help everyone meet their deadlines.
Use the right collaboration tools
Working from home has its own challenges and issues, so try to find the right tools for the job. Slack, Skype, Zoom, Hangouts are great for communication. Design tools such as Figma or Sketch cloud help you communicate designs quickly and effectively. GitHub is the perfect tool for code collaboration and VS Code has an extension (Live Share) for sharing your code editors. Communicate with your team, figure out your needs and pick tools that work for you.
Maintain regular hours
It's as easy to forget about breaks as it is to start working late. Try to set a schedule that sits well with your team and stick to it. If you would work 9 to 5 in a regular office environment, do the same at home. Take regular breaks, do not forget to sign off when put in your daily hours and come back tomorrow. After all, it's business as usual.
Snippets Articles S Art Of Debugging Meditation (`content/snippets/articles/s/art-of-debugging-meditation.md`)
title: Meditation and the art of debugging tags: [webdev,programming] cover: meditation-1 excerpt: What's the connection between meditation and debugging? Let's find out. listed: true dateModified: 2025-08-28
What is the common characteristic between meditation and debugging? While two seemingly unrelated activities, both require focused attention, patience, and a non-judgmental awareness to observe and resolve what arises. Let's dive deeper into this intriguing connection!
Debugging as Meditation
Both in meditation and debugging, the key lies in cultivating a state of awareness and presence. In order to get the most out of it, one has to pay close attention to what's going on. In debugging, this means observing code behavior, while in meditation it means observing thoughts, feelings and reactions.
But the parallel doesn't stop there; in fact, it goes much, much deeper. Both practices require you to explore the root cause and underlying patterns. In meditation, this might involve delving into the sources of stress or distraction, while in debugging, it means identifying the root cause of a bug or issue.
A non-judgmental attitude that allows for exploration and discovery is also beneficial. Observing thoughts and feelings without attachment equates to analyzing code behavior without jumping to conclusions.
Patience and persistence are also key, as both meditation and debugging can involve prolonged periods of focus and introspection. It's important to approach each session with a sense of curiosity and openness, allowing insights to emerge naturally.
Finally, both bear similarities in their outcomes. Both practices can lead to a greater sense of clarity, understanding, and resolution. Apart from fixing an issue, you often attain clarity of thought and a deeper understanding - of code systems or oneself - through the process, fostering professional or personal growth.
Wu Wei
By now you may be convinced that debugging does indeed bear similarities to meditation. In fact, you may even be tempted to approach it in a similar mindset. But how does one make the most of this insight?
The answer, in my opinion, might lie in the Taoist concept of Wu Wei (无为), meaning "effortless action" or "non-doing." It's about aligning with the natural flow of things rather than forcing outcomes.
When debugging, Wu Wei manifests in a few different ways, that act as useful principles for developers:
Let go of forcing solutions
Just as in meditation, where one learns to let go of the need to control thoughts, in debugging, it's essential to release the urge to force a solution. This means allowing the debugging process to unfold organically, without imposing preconceived notions about what the solution should be.
Observe without attachment
In meditation, practitioners learn to observe their thoughts and feelings without becoming attached to them. Similarly, in debugging, it's important to examine the code and its behavior, without labelling it or yourself as "bad", "flawed", "broken" or "incompetent". Accept the system as it is and don't jump to conclusions. This allows for a more objective analysis of the issue at hand.
Act in harmony with the system
Wu Wei encourages acting in harmony with the natural flow of the world. In debugging, this means making changes that feel intuitive and minimal, intervening only as much as needed. You trust your understanding, make the smallest possible fix, and let the system guide your next steps.
Embrace pauses and stillness
Sometimes, stepping away or taking a mindful pause leads to insight. Wu Wei emphasizes the power of stillness. Solutions often arise when you're not actively searching for them. In debugging, this might mean taking a break, meditating, or simply sitting with the problem for a moment.
Trust the process
Finally, Wu Wei is about trusting the process and having faith that insights will emerge when the mind is calm and receptive. Allow the subconscious mind to work on the problem. Often, solutions become clear when you least expect them. Trust that clarity will come. Progress doesn't always look like constant action.
Practical tips of meditative debugging
If all of that sounds like hodgepodge to you, here are some practical tips that I find helpful and might be more actionable to you:
- Create a calm environment: Minimize distractions and create a workspace that promotes focus and tranquility.
- Practice mindfulness: Take a few moments to center yourself before diving into debugging. Focus on your breath and clear your mind.
- Embrace curiosity: Approach debugging with a sense of curiosity rather than frustration. Ask questions and explore different angles.
- Take breaks: Step away from the code when feeling stuck. A short break can provide new insights and perspectives.
- Reflect on patterns: After resolving an issue, take time to reflect on the process. What worked? What didn't? This reflection can enhance future debugging efforts.
Benefits of a Wu Wei-inspired approach
Finally, you might be thinking what's the value of trying such an approach?. Sure, you can go your whole life without trying it, but there are some compelling benefits to consider:
- Reduced frustration: By allowing solutions to emerge naturally, developers can avoid the stress and frustration that often accompany debugging.
- Greater insight: Mindful observation and allowing intuition to guide you, you gain a deeper understanding of the problem space.
- Enhanced focus: The meditative approach cultivates deep concentration, helping you notice subtle clues and avoid tunnel vision.
- Improved problem-solving: Non-attachment frees you from preconceived notions, making you more open to unlikely solutions.
- Personal growth: The reflective nature of this approach fosters continuous learning and self-improvement.
Conclusion
By integrating the principles of meditation and Wu Wei into the debugging process, developers can cultivate a more mindful, creative, and effective approach to problem-solving. Embracing these practices not only enhances individual debugging sessions but also contributes to a healthier, more balanced relationship with code and technology.
Snippets Articles S Avoiding Bad Advice (`content/snippets/articles/s/avoiding-bad-advice.md`)
title: Beware of Bad Programming Advice - Spotting Senior Juniorism shortTitle: Avoiding Bad Programming Advice tags: [webdev,career,programming] cover: inside-weather excerpt: Don't fall victim to bad advice! Learn to identify "senior juniorism", and develop critical thinking to make better coding decisions. listed: true dateModified: 2023-06-04
Aspiring programmers are always looking for advice, especially from experienced developers who have walked the same path. However, not all advice is created equal. In fact, some advice can be downright dangerous, leading to poor coding practices and bad habits. This is where the phenomenon of "senior juniorism" comes in – where juniors write too much about the few things they know, completely oblivious to their own blind spots.
How we got here
If I had to guess, I'd blame the fake it till you make it mentality first and foremost for this situation. While this phrase can be motivating, it's often taken too seriously, leading to bad advice and misinformation spreading like wildfire. When someone pretends to be an expert, they may inadvertently give advice that is outdated, incomplete or simply incorrect.
Another culprit is the fact that many developers are self-taught. The internet has given us access to a wealth of information, but it's also made it easier than ever to skip over the basics. This can be especially dangerous when it comes to security, where a single mistake can lead to a major breach.
Last but not least, lack of experience can also be a factor. While it's true that experience is not everything, it's more likely that you'll be exposed to different situations, problems and opinions over time, which will help you form a more balanced view of things. This sort of exposure is something that you simply don't have when you're just starting out.
Combining all of this with the learning in public mentality that is often pushed to new developers and things can easily get out of hand. Thus, it's imperative that we learn to spot bad advice.
Spotting bad advice
To avoid falling for bad advice, it's important to understand the three types of knowledge: what you know you know, what you know you don't know, and what you don't know you don't know. It's important to acknowledge that there are many things we don't know, and be humble enough to admit it. Even the most experienced developers have gaps in their knowledge.
So how can you avoid falling for bad advice? One way is to pick reputable sources, such as books, articles, or experts in the field. However, even reputable sources can be biased or outdated. That's why it's important to do your own research, experiment with code, and develop your critical thinking skills.
Some self-reflection
As my writing (and this very post) is a source of advice in itself, I suggest you take it with a grain of salt, too. I'm not immune to this phenomenon and I've made many mistakes in the past, hopefully without causing too much harm. I've also followed and reproduced bad advice at times, privately or publicly. Here's a few examples of bad advice I've fallen for:
- Coercion is always bad, except it isn't. It's a language feature that can be useful in certain situations and any confusion around it stems from not fully understanding how it works.
- Never use classes in JS, because they're syntactic sugar on top of prototypes. This might be true, but to a beginner coming from an object-oriented background, it feels a bit like gatekeeping.
- Avoid useEffect in React, opting for third-party hooks instead. Those hooks are certainly useful, but they use
useEffectunder the hood, so you'll eventually need to learn about it one way or the other.
I've definitely made my fair share of mistakes, and I'm sure I'll make many more in the future. However, I'm always trying to learn from them and improve my writing. I'm also trying to be more mindful of the advice I give, and I hope you'll do the same if you're sharing your knowledge with others.
Conclusion
In conclusion, senior juniorism can be a dangerous phenomenon that can lead to bad advice spreading To avoid falling into this trap, it's important to acknowledge the limits of your knowledge, pick reputable sources, do your own research, and develop your critical thinking skills. Remember, not all advice is created equal, and it's up to you to make informed decisions based on the specific context.
Snippets Articles S Bash Alias Dollar Sign (`content/snippets/articles/s/bash-alias-dollar-sign.md`)
title: A bash alias for copying commands from the web shortTitle: Bash alias for copying from the web language: git tags: [configuration] cover: capsule-coffee excerpt: Learn how to eliminate the annoyance of copying dollar signs ($) along with terminal commands from the web with a simple bash alias. listed: true dateModified: 2023-03-05
If you've ever copied a terminal command from the web, chances are you've also copied the dollar sign ($) that precedes it. This is a small annoyance that many developers encounter almost daily, but it can be easily dealt with. All you need to do is add an alias for the dollar sign to your .bashrc or .zshrc file:
alias '$'=''But what about variables? Aren't these prefixed with a dollar sign? This bash alias won't break your scripts, as aliases replace only the first word of each simple command. Simply put, the only dollar sign that will be replaced is the one at the beginning of the line.
Snippets Articles S Benefits Of Writing (`content/snippets/articles/s/benefits-of-writing.md`)
title: The benefits of writing tags: [webdev,career,programming,jobs] cover: laptop-view excerpt: Writing about a topic can often sound boring or worthless. But there are significant benefits to doing it from time to time. listed: true dateModified: 2022-11-05
I was recently tasked with writing a design document at work. What first seemed like a tiresome chore I had to endure turned out to be both enlightening and beneficial both to me and my team. This isn't just a case of making the best of a bad situation, but rather a case of rediscovering the reason behind why writing down your thoughts isn't an exercise in futility. Instead of boring you with a long-winded story about the task, I'll just present the takeaways and hopefully do some storytelling in the process. So why should you write then?
You learn more about the topic at hand
Presenting a topic requires meticulous research in order for you to be able to write down the most interesting and relevant findings. This process will uncover any gaps in your knowledge of said topic, gaps your research will in turn have to cover. I've seen this numerous times in the past while doing research both for this website as well as tasks at work and it always holds true. If nothing else, you will gain a deeper understanding of whatever it is you are presenting, which will be useful sometime in the future. In the case of my task, I understood more about the business logic, how premature optimizations got in the way of stability and why it was so hard to consistently reach the desired state, as I discovered a hidden layer of complexity I didn't know about before.
You discover the root of the problem
Most of the time we assume to know the problem well enough to try and solve it. A lot of the time, I've found out, I know a lot less than I originally thought about the domain, the problem and, more importantly, the root cause of it. Again, presenting the problem to a reader will put you through the process of having to explain it and boil it down to the key details. This more often than not results in a concise description of whatever is at the root of the problem. Working on my design document, I figured out that the root of the problem was essentially a set of bad assumptions that kept piling up and had some mismatches between the backend and frontend code.
You understand past decisions
This one applies to problems you aren't the first one to tackle, but might still be relevant in other situations. As you go you will come across whatever implementation details you didn't know about. If you take a closer look, you'll figure out how those came to be in the first place and why those decisions were made in the past. Understanding the reasons behind past decisions is crucial in figuring out if the original set of needs, restrictions and assumptions is still valid and how to amend them if necessary. As I was already knee-deep in this task, I realized that the decisions we made almost one year prior were valid at the time, decisions I didn't fully understand back then. A couple of them, however, didn't seem to stand the test of time as business needs changed and we'd have to update them accordingly.
You come up with more varied solutions
Understanding past decisions, covering the gaps in your knowledge and boiling down the problem to a concise description you should already be a lot more informed about the topic you are researching than before. Hopefully at this stage, you will have an itch to try a different solution from the one you originally envisioned or you'll have come up with a solution where there was none. More varied solutions are always better because you can compare tradeoffs and get a better understanding of what's best. In my case, I ended up considering a solution I had mostly dismissed originally. And it was that solution which made the most sense to me in the end, even though it was more radical than the one I was planning to propose originally.
As a closing thought, try writing even if nobody is going to read it. Write it for you. It will help you understand, explain, reason about and tackle the task all at once. It's not a waste of time, it's a powerful tool you could leverage from time to time.
Snippets Articles S Code Modularization (`content/snippets/articles/s/code-modularization.md`)
title: You Shouldn't Always Modularize Your Code shortTitle: Case Against Modularization tags: [webdev,career,programming] cover: salad-1 excerpt: Have you ever stopped to wonder if modularization is right for your use case? What if I told you it might not be? listed: true dateModified: 2025-05-10
Modularization is often hailed as a best practice in software development. It promises cleaner codebases, easier maintenance, and better scalability. However, modularization is not a one-size-fits-all solution. While it shines in large-scale projects with complex requirements, it can become a burden in smaller projects or early-stage development. In this article, we'll explore why you shouldn't always modularize your code and when sticking to a monolithic approach might be the better choice.
When Modularization Works Best
Modularization is undeniably powerful in the right context. For large projects, where individual modules are already substantial and require independent maintenance and testing, modularization can help manage complexity. Breaking down a massive codebase into smaller, self-contained modules makes it easier to:
- Assign ownership to different teams.
- Test and deploy modules independently.
- Scale the application by focusing on specific areas.
For example, in a large e-commerce platform, separating the payment gateway, product catalog, and user authentication into distinct modules makes sense. Each module can evolve independently, and teams can work in parallel without stepping on each other's toes.
// Example: Modularized payment gateway
// filepath: /modules/payment/index.js
export const processPayment = (amount, method) => {
// Payment processing logic
console.log(`Processing ${amount} via ${method}`);
}
// filepath: /modules/payment/validation.js
export const validatePaymentDetails = (details) => {
// Validation logic
return details.cardNumber && details.expiryDate;
}However, not every project is a massive e-commerce platform. For smaller projects, modularization can introduce unnecessary complexity.
The Pitfalls of Premature Modularization
Early and aggressive modularization is often a sign of premature optimization. Developers may feel compelled to break their code into modules from the start, thinking it will save time later. In reality, this can lead to:
- Increased complexity: Managing multiple modules, even for a small project, can be overkill. You'll spend more time setting up and maintaining the structure than writing actual code.
- Testing nightmares: Interdependent modules can make testing a headache. Changes in one module might break another, leading to cascading issues.
- Ecosystem bureaucracy: Rolling out releases for multiple modules, tracking changes, and managing dependencies can eat into your productivity.
For example, imagine a simple blog application. Splitting it into separate modules for posts, comments, and users might seem like a good idea. But for a small team or solo developer, this adds unnecessary overhead.
// Monolithic approach for a small blog app
const createPost = (title, content) => {
console.log(`Post created: ${title}`);
}
const addComment = (postId, comment) => {
console.log(`Comment added to post ${postId}: ${comment}`);
}
const registerUser = (username) => {
console.log(`User registered: ${username}`);
}
// All logic in one place, easy to manage for a small projectDebugging and Security Challenges
One of the biggest downsides of modularization is the cascading effect of changes. Debugging an issue in an external module can lead to a chain reaction of updates, releases, and deployments across multiple repositories. This is especially frustrating when dealing with security vulnerabilities. While modularization can make dependency updates easier, it also means you have to track and patch vulnerabilities across all modules.
For example, if a critical bug is found in a shared utility module, you'll need to:
- Fix the bug in the utility module.
- Release a new version of the module.
- Update all dependent modules to use the new version.
- Test everything to ensure nothing breaks.
This process can be time-consuming and error-prone, especially for small teams.
Monorepos: A Partial Solution
Monorepos are often touted as a way to mitigate the challenges of modularization. By keeping all modules in a single repository, you avoid the hassle of managing multiple repositories. However, monorepos don't eliminate the underlying problems of modularized code. You still have to deal with interdependencies, testing, and cascading changes.
For smaller projects, monorepos might simplify some workflows, but they don't justify modularization if the project doesn't need it in the first place.
Why Monoliths Make Sense for Smaller Projects
For smaller projects, side projects, or tiny teams, sticking to a monolithic architecture is often the better choice. A monolith keeps everything in one place, making it easier to:
- Understand the codebase.
- Debug issues without worrying about inter-module dependencies.
- Deploy the entire application as a single unit.
If modularization becomes necessary later, it will happen naturally. For example, if a piece of code becomes reusable across multiple projects, you can extract it into a module when the need arises.
// Example: Extracting reusable code later
// filepath: /utils/formatDate.js
const formatDate = (date) => {
return new Date(date).toLocaleDateString();
}
// filepath: /app.js
import { formatDate } from './utils/formatDate.js';
console.log(formatDate('2025-04-19')); // Reusable utilityThis approach allows you to focus on building the application first, without getting bogged down by unnecessary structure.
Conclusion
Modularization is a tool, not a rule. While it's invaluable for large-scale projects, it can be a burden for smaller ones. Premature modularization often leads to increased complexity, testing nightmares, and productivity loss. For smaller projects, monoliths are usually the better choice, offering simplicity and ease of management.
Remember, you can always modularize later if the need arises. Start simple, and let your project's requirements guide your architecture. By avoiding unnecessary modularization, you'll save time, reduce frustration, and focus on what really matters: building great software.
Snippets Articles S Design Systems Reality (`content/snippets/articles/s/design-systems-reality.md`)
title: Design systems rarely survive contact with the real world shortTitle: Design systems tags: [webdev] cover: tranquil-riverscape excerpt: Design systems are often seen as the holy grail of design and development, but there are a few issues that can arise in the real world. listed: true dateModified: 2025-06-14
Over the years, both as a professional and as a hobbyist, I've used, maintained and developed a number of design systems. While the promise of a unified, reusable set of components is alluring, my personal experience has shown that design systems rarely survive contact with the real world. Instead of being a panacea, they often become a source of friction, slowing teams down and introducing new challenges that are rarely discussed outside of closed-door retrospectives.
[!IMPORTANT]
This is an opinion piece. The views expressed here are my own and may not reflect those of the organizations I work for or with. As opinions often tend to be subjective, I encourage you to take them with a grain of salt.
What design systems are for
The promise of design systems is simple: create a shared language and toolkit that designers and developers can use to build consistent, scalable products. In ideal scenarios, design systems provide clear benefits: faster onboarding, fewer inconsistencies, and a foundation for rapid iteration. When done right, they can be a powerful force multiplier for teams.
The reality of design systems
Yet, the real world is full of use cases that are messy, complex, and unpredictable. The gap between theory and practice quickly becomes apparent. You simply can't design for every scenario, and the importance of flexibility and adaptability is often underestimated. Design systems tend to be rigid and verbose, making them hard to use in practice. They're almost never complete, especially in projects that evolve rapidly and dynamically.
Escaping isolation
A problem I've noticed is that design systems are often created in isolation, as an attempt to organize, or worse yet reorganize, a messy project. This is especially problematic when replacing existing designs and code, since many edge cases are unknowable to the design team.
Moreover, limited capacity during replacement can lead to half-baked solutions, or systems applied only to parts of the codebase. Supporting multiple platforms and environments introduces further complications, as do the idiosyncrasies of the codebases or platforms in question.
Organization-wide alignment is also an uphill struggle. People resist change, and any shift in the team can affect the vision and direction of the design system, leading to inconsistencies and confusion.
Development is hard
Nobody is surprised to hear that software development is inherently complex, and good, organized design is only one facet of the process. This leads to a situation where tools and solutions are often bent out of shape out of necessity, not incompetence or negligence.
Cracks often first appear in convention discrepancies (e.g. component names) between design and development, then propagate into implementation details. Developers, faced with a tangled web of issues, will often resort to hacks that pile up over time. Designers aren't immune either; they may abuse components to make their work easier or find shortcuts. The underlying code often doesn't match the design system, and external component libraries can slow down development, requiring specialized changes to adapt to new constraints.
Time is the enemy
Small changes tend to accumulate with time, resulting in deviations that are hard to track. Patches and edge case solutions in the code may or may not be communicated to the design team, but don't always make their way back into the designs. A chat in the hallway or a quick Slack message is bound to be forgotten when there's a deadline looming.
Over time, deterioration and a proliferation of edge cases can make even the best design systems unwieldy. As changes pile up, so does technical debt, which in turn leads to more hacks and workarounds. The design system becomes a burden rather than a tool, and the original vision is lost in the noise.
Productivity & velocity
Product teams are under constant pressure to deliver, and contributing back to the design system is rarely a priority. On the fly decisions may never be added to the system, but get replicated throughout the codebase. Alignment with the design system can bog down the fast iterations that product management demands, and the system itself may be seen as a hindrance to productivity rather than a tool to enhance it.
A similar situation arises for hobby projects, open source and small teams. The initial excitement of creating a design system can quickly fade when faced with the reality of maintaining it. The time and effort required to keep it up to date can be overwhelming, especially when the focus is on delivering features and fixing bugs, or when time to work on a side project is limited due to other commitments.
Conclusion
Design systems are a great idea in theory, but they often fail to deliver the expected results in practice. It's better to focus on the principles of good design and development, rather than enforcing a rigid system — especially in fast-paced environments with constantly evolving codebases.
Good communication is key to success in larger teams, while for small teams or solo projects, a design system may end up being overkill. Many of these issues also apply to code architecture and design documents, and the solutions are often similar: communication, flexibility, and adaptability.
Still, there's always room for improvement: a design system can be a valuable tool if adopted slowly, iteratively, and with the right mindset. Ultimately, they require careful consideration and a willingness to adapt to the realities of software development, both from a design and a development perspective.
Snippets Articles S Dns Record Basics (`content/snippets/articles/s/dns-record-basics.md`)
title: A beginner's guide to DNS records shortTitle: DNS Record Basics tags: [webdev,dns,server,cheatsheet] cover: sparkles excerpt: DNS records may not be something you work with frequently, but having a basic understanding can be highly beneficial. listed: true dateModified: 2022-01-09
Most web developers go about their day-to-day without having to deal with DNS records most of the time. Regardless, knowing what DNS stands for and the types of DNS records are pretty useful.
DNS Definition
The Domain Name System (abbreviated to DNS), translates human-readable domain names (e.g www.google.com) to machine-readable IP addresses (e.g. 142.250.186.46).
DNS Records
A DNS is made up of multiple records of different types, each one with its own purpose. Here's a breakdown of the most commonly-used ones:
- A record: The address record. Used to map a domain name to an IPv4 address. Similarly, the AAAA record is used to map a domain name to an IPv6 address.
- CNAME records: A canonical name record. Creates an alias that points to another domain or subdomain, but never an IP address.
- ANAME record: Allows you to point the root of your domain to a hostname or a domain name.
- TXT records: Allow the addition of limited text notes and is often used for ownership verification purposes, validation or security.
- MX record: Specifies the mail server responsible for accepting the incoming and outgoing emails for a domain. Should point to a mail server name, not an IP address.
Snippets Articles S Escaping Tutorial Hell (`content/snippets/articles/s/escaping-tutorial-hell.md`)
title: Escaping tutorial hell tags: [webdev,career,programming,jobs] cover: campfire excerpt: Many beginners get stuck in tutorial hell without even realizing. Here are 4 simple ways to escape tutorial hell and get back to learning. listed: true dateModified: 2022-05-26
Tutorial hell refers to the stage in your learning journey, where you are following along with tutorials, not truly learning that much. Tutorials are by nature a curated experience. While that's not definitively bad, it's not always the best way to explore the landscape and familiarize with concepts and tools. Neither is it the best way to learn how to think for yourself. So how can you escape it?
Debug
The most common exit ramp comes up when you end up in debugging mode. Oftentimes a tutorial can be outdated or something in the setup can be just a tiny bit different. At this point, you have to figure out how to solve the issue to proceed.
Entering debugging mode, you realize that you know only a part of the big picture. What you don't know might not be immediately obvious, but with perseverance you will be able to figure it out. At that point, you can start looking for answers to solve the issue.
Debugging a problem offers a little bit of exposure to the underlying technologies and concepts. This way you can dip your toe into whatever it is you are working with, without getting overwhelmed.
Be curious
Curiosity killed the cat, but not you. Stop every step of the way to ask what it is you are learning. Do you understand the concepts? Do you know what it is you are building? Is this tool your only option? What problem does it solve? Are there others?
Questions are a valuable tool to help you explore the landscape. You don't necessarily need to answer them all, nor do you need to read tons of documentation or articles about a topic. Simply taking a peek at the big picture and asking questions is enough. You can always come back later and dive deeper.
Experiment
The third and best way to escape tutorial hell is to experiment. After you finish a couple of tutorials, put your skills to the test. Try to build something from scratch, looking up anything you need to, as you go. It won't be as easy or as fast as the tutorial was, but you'll learn a lot more.
After you build a project, take another look. Figure out what you could have done differently or what parts of it you don't understand well enough. Refine it, polish up parts of it, add new features. The more time you put into it, the better you will become.
Then do it a few more times and experiment with a few more ideas. As soon as you start working on your own projects and get into a rhythm, it's a lot easier to learn new things. Some slopes will be steeper and others will be more gentle. But you will feel much more confident in your skills than blindly following tutorials.
Learn your own way
As a closing suggestion, I want to urge you to find your own learning style. Tutorials might be right for some and I don't suggest you dismiss them entirely. After all, we all have to start somewhere. But if they don't work for you, mix it up a bit. Try reading the documentation, solving coding exercises or tinkering with an existing project. Everyone is different and there's no one size fits all when it comes to learning.
For some, much like myself, teaching is the best learning experience. After all, if you can't explain something simply, you probably don't understand it well enough. Explaining, then, can lead you to researching, debugging, experimenting and being curious all at once. And teaching, much like learning, can be done in many many different ways.
Snippets Articles S Explicit Code Is Always Better (`content/snippets/articles/s/explicit-code-is-always-better.md`)
title: Explicit code is always better language: javascript tags: [webdev] cover: laptop-notes-2 excerpt: A thesis on why magic abstractions are harmful to codebases and teams. listed: true dateModified: 2025-11-04
I recently had the misfortune of working with a little-known JavaScript library and boy did I hate it! From naming conventions that are hard to follow to abstractions that feel like black magic, in my mind it's exactly what's wrong with overengineered tools and a justification for a move back to explicit, albeit potentially verbose and boilerplate-heavy, code.
I know what you may be thinking but hear me out: explicit code is always better. It beats magic. It makes intent obvious. It reduces time spent chasing down nasty bugs.
Readable by design
Implicit behavior can feel clever. It can also be a silent source of mistakes. When a framework maps files to routes or renames properties automatically, you must remember the transformation. You must trace it across files and sometimes across languages.
Explicit code shows the path. An explicit import, export, or variable name tells the reader where a value comes from and how it is used. That clarity is worth a few extra lines.
// Hard to follow
const result = data.
map(item => item.value).
filter(v => v > 10).
reduce((a, b) => a + b, 0);
// Easier to follow
const values = data.map(item => item.value);
const filteredValues = values.filter(v => v > 10);
const result = filteredValues.reduce((a, b) => a + b, 0);When writing code with maintainability in mind, prefer a named intermediate variable to a long chain of anonymous transforms. The name becomes a breadcrumb for future readers. Remember that clever one-liners can be elegant. But, they can also be cryptic. Favor explicit, readable implementations over terseness when clarity matters. It also makes the code easier to test and reuse.
Types for clarity
I've recently dabbled in Go and I was surprised how intuitive I found its type system and how much use I got out of it. While I used to dislike the rigidity of static types, I have now come to appreciate their value in making code more predictable and easier to maintain.
Static typing documents intent. It signals which fields are required and which are optional. It narrows the space of valid values and makes refactors safer.
type User = {
id: string;
name: string;
email?: string;
};
function findUser(id: string): User | null {
// implementation
return null;
}Sure, I'm not a fan of TypeScript, but my gripes with it are entirely separate from the value of types in general. Types make a contract explicit. You do not have to search for implicit assumptions across modules.
Traceable imports and exports
We often despise adding all the wiring, boilerplate and ceremony. Sure, it's verbose and it can feel mind-numbingly boring. But, those lines are discovery points for anyone who reads the code later, including our future selves.
Go forces explicit imports and simple module boundaries. That constraint is, oddly enough, an advantage. It makes dependencies obvious. It lowers the barrier to entry for new contributors.
// Explicit imports
import express from 'express';
import userRoutes from './routes/user.js';
const app = express();
app.use('/users', userRoutes);
// Explicit exports
export default app;Explicit wiring creates followable links. You can trace imports, types, and function calls back to the source. That makes it easier to find where a value is created or transformed.
You'll eventually have to trace back through the code to find something. And, when you do, you'll be grateful that you did the work upfront, spending those minutes that felt like a waste to write something more explicitly.
Conventions increase cognitive load
Conventions can be useful. They cut boilerplate, but they also hide behavior. When naming conventions travel from the server to client code, or when build tools transform property names, those mappings become invisible mental work.
Context switching between languages already costs attention. Adding implicit transforms multiplies that cost. Choose explicit transformations when you can. If you must sprinkle some magic, document it and add tests that show the mapping.
import { deepCamelizeKeys } from './deepCamelizeKeys.js';
// server response: { user_id: 1, user_name: 'Alice' }
// Implicit transformation, via convention
const user = fetch('/api/user')
.then(res => res.json())
.then(deepCamelizeKeys);
// Explicit transformation, key by key
const user = fetch('/api/user')
.then(res => res.json())
.then(data => ({
userId: data.user_id,
userName: data.user_name,
}));Every hidden rule increases the mental model a developer must hold. Projects with many implicit rules slow down onboarding. They make debugging and refactoring riskier. They also make code reviews harder.
Make explicit code the default and only reach for conventions when they provide a clear benefit. Ensure they are few, well documented, and widely understood.
[!TIP]
As a rule of thumb, if a convention feels like magic and obscures behavior, avoid it. The short term gain rarely justifies long term cost.
Abstractions as barriers to entry
Abstractions remove repetition. They also hide details. As a library grows, the abstraction surface usually expands. That expansion increases the cost of learning and debugging.
Junior developers and newcomers face the steepest penalty. Do not reject boilerplate reflexively. If repeated, explicit code becomes a productivity sink, build a small, well documented abstraction.
// Implicit state management, hidden state
const countert = (initial = 0) => {
let value = initial;
return {
increment: () => { value += 1; },
decrement: () => { value -= 1; }
};
}
// Explicit state management, more visible state
class Counter {
constructor(initial = 0) {
this.value = initial;
}
increment() {
this.value += 1;
}
decrement() {
this.value -= 1;
}
}Explicit languages, like Go, tend to encourage careful abstraction. Teams only reach for abstraction when the repetition justifies it. Reach for an abstraction when it provides a clear benefit. Keep abstractions small. Document their invariants. Add tests and provide examples. Keep an escape hatch so developers can bypass the abstraction when necessary.
Practical rules to apply today
- Prefer explicit exports and imports in your code to make it easier to trace dependencies and follow data flow.
- Use static types, if possible, or at least add type checks whenever they make sense to help clarify intent and catch mistakes.
- Avoid cross language implicit transformations that add cognitive load, unless they are few, truly necessary, and thoroughly documented.
- Prefer readable functions and named variables over clever one-liners and chains of anonymous transforms.
- Document any convention that behaves like magic and add tests showing the mapping to reduce the barrier to entry.
Closing thoughts
I hope I have at least changed your mind about the value of explicit code. It may feel like a chore to write, but it pays off in the long run. Make clarity your default. Start explicit. Add convenience when it is necessary and safe. When you do, document and test the shortcut so future readers do not have to guess.
Snippets Articles S How 30 Seconds Of Code Is Deployed (`content/snippets/articles/s/how-30-seconds-of-code-is-deployed.md`)
title: How 30 seconds of code is deployed tags: [webdev] cover: home-office-3 excerpt: A historical run-through of how 30 seconds of code was deployed then and now. listed: true dateModified: 2025-07-03
This is perhaps the longest article I've written, but I feel there's a ton of value to be had from my insights, mistakes, and the evolution of how 30 seconds of code has been deployed over the years. Grab a cup of coffee, and find a quiet spot, as we take a trip down memory lane.
[!TIP]
To view the state of the repository at a given time in history, you can visit the Commit Reference and Browse files.
Markdown-only Early December 2017
Tools: Node.js · Commit reference
The original build process involved around the generation of Markdown with all of the snippets ordered and organized as a Markdown file with internal links presented as a page of contents.
Some static Markdown parts were used for the file's header and footer and a builder script would read each file in order and add its contents to an output string. The final result would be written to the repository's README.
Additionally a linter script was also provided to apply some predefined rules (Semistandard) to the code in the Markdown files.
As far as automation went, it was completely non-existent, needing manual script runs and commits for each build.
Earliest website attempts Late December 2017
Tools: Node.js, HTML, Travis CI, GitHub Pages · Commit reference
As time went by, the 30 seconds of code bot account (not a real bot) was introduced. The account was used for automation tasks, such as automated builds via the creation of a GitHub Token that was then used to push commits to the repository. The following git push command was what allowed the bot to push new code to the repository:
git push --force --quiet "https://${GH_TOKEN}@github.com/Chalarangelo/30-seconds-of-code.git" master > /dev/null 2>&1Using the token, stored in an environment variable on GitHub, as part of a special URL one can push code from the token authorizer's account to the repository (provided the author has sufficient access). The rest of the command is written in such a way to prevent outputting the token into the logs and exposing a backdoor that anyone could exploit.
For the purpose of automation, Travis CI was used. This allowed changes pushed to master to trigger new builds, as well as other automation tasks. The configuration file for Travis specified which tasks to run (such as linting via Semistandard and Prettier) and scripts to execute.
Tagging was added during that time in the form of a plaintext file, specifying a tag for each individual snippet. A tagging script was added as well, allowing new snippets to be added to the file as uncategorized, which then needed manual processing.
Alongside the previous build script, a new web script was added. It followed a very similar pattern, generating an HTML page, clearly inspired by Lodash's website. The output of the web script was then stored in a directory and deployed automatically on GitHub Pages.
The current domain was obtained during this time from Namecheap, but unfortunately the configuration that was used back then is lost to time.
Trivia
Travis CI was misconfigured for a little while, resulting in Travis builds triggering new builds as soon as the pushed to master. This caused an infinite loop that burned through the entirety of the free credits the project was provided with. Luckily, the Travis team was kind enough to restore them after I messaged them.
GitHub Issues malfunctioned at some point, resulting in the issue counter jumping a few numbers (something like 100). To this day, no incident report has been published and GitHub has not explained what happened back then.
The bot token was originally incorrectly added in plaintext as part of the bash script where it's used. This resulted in a security vulnerability that was identified by someone in a chatroom. Luckily, no malicious actors took advantage of it and I generated a new token after the code was fixed. If I remember correctly, a full rewrite of the master branch was performed to purge the offending commits.
Multi-page website Early to mid 2018
Tools: Node.js, HTML, Travis CI, GitHub Pages · Commit reference
As the project grew, several new ideas were tested. Alongside website build automation, a Rollup script was used to create a JS package with all the code snippets and tap was used to add test cases for each snippet. These were part of the automated workflows running still on Travis CI. The JS package was also released on npm, but was discontinued at a later time.
New static parts were added to split the page into multiple pages at this point, and a redesign was implemented to make the page a little better visually, showcasing statistics and contributors. Some dynamic content was added, such as daily picks on the homepage and data retrieved via GitHub's API, using a GitHub token same as other processes.
Finally, Travis CI was set up to run using a cron job schedule as well as push events on the master branch, allowing for daily builds to occur at a predetermined time.
Trivia
2018 was a year of experimentation. New side projects came and went, such as a localization effort, a JS glossary and a snippet archive. Traces of these experiments can be found in the commit history of the repository.
At this time, the website was only comprised of the JS snippets and nothing else. Similar projects for CSS and Python were actively supported by some of the contributors, but had full autonomy to pursue their own paths. This decentralized organization put together without much planning would later come to bite me.
Website facelift Late 2018
Tools: Node.js, HTML, Travis CI, GitHub Pages · Commit reference
Later in 2018, the website was redesigned yet again. The dynamic parts were removed, the search system was reintroduced and the website style's were finally completely custom, written in Sass.
Not much had changed in the automated workflows, except for the introduction of some intermediary steps. One such step was the creation of a JSON file via the extractor script, which could then be used as the data source for other tasks. The Travis CI configuration file now contained about a dozen scripts and this was a way to speed up a lot of them.
Another experiment that was running at the time was a snippet collection for VS Code, generated by another script. Unfortunately, publishing the collection could not be automated and required a very slow process every time a new version needed to be published. This was also discontinued later down the line.
Trivia
DigitalOcean sponsored the project back in 2018, providing a handful of cloud machines to work with. Unfortunately, this sponsorship was never utilized and the website never got the much awaited snippet API that it was expected to get. Another sponsorship from DigitalOcean came through the next year, but unfortunately it suffered the same fate.
Gatsby introduction Late 2019
Tools: Gatsby, React, Travis CI, Netlify · Commit reference
The first half of 2019 was mostly quiet. However, during the summer months, Gatsby was all the rage, so naturally 30 seconds of code followed the hype. It was also a great opportunity to move towards using React instead of plain HTML, which was getting hard to work with.
Instead of GitHub Pages, Netlify was now the platform where the website would be deployed. This proved to be a good plan, as it allowed Travis CI to execute the other scripts, whereas Netlify would run Gatsby and deploy the website.
At this time, most side projects about other languages were starting to fall apart. The 30-seconds organization seemed like the way forward, allowing consolidation of all official repositories and proper maintenance. Some integration tools and a repository starter were provided, allowing new projects to run autonomously.
Each repository maintained its own website instance, all under the same domain name, but individual subdomains (e.g. css.30secondsofcode.org). The infrastructure was pretty much the same for each repository, with unique elements such as logos and styles being maintained for each project. Subdomains were configured via the Namecheap Advanced DNS menu, linking each Netlify project to an appropriate subdomain.
While this allowed for every project to be presented in a similar manner, it complicated maintenance to the point where a simple text change would require 5 or more commits across different repositories. This was definitely non-feasible in the long-term.
At around this time, HTTPS was becoming mandatory for all websites to be trusted by users and browsers. I'm not 100% certain how I obtained my SSL certificate before this time (I think I used Cloudflare at some point, but the details are lost to time), but Netlify is kind enough to provide you with SSL certificates for each website for free. Obviously, I used their service as soon as it was available to me and never looked back.
Trivia
A lot of commits during that time are from unrelated histories (other repositories). You can probably tell by looking at the README logo, which features a different 30 seconds stopwatch for each project.
Getting the rights to all the repositories was not easy. Some maintainers wanted to retain their autonomy, others were completely indifferent and some were nowhere to be found. One of the repositories' original owner had disappeared from GitHub and it took a lot of searching to track the down on Reddit and ask them to transfer the repository to the organization. Luckily, nobody had any issues with the transfer and the organization filled up with content.
During this time or a little while later, Travis CI was starting to move away from its free forever model, making it harder to keep using. Eventually, credits ran out and Travis CI was dropped entirely from the infrastructure.
Website consolidation Early 2020
Tools: Gatsby, React, Travis CI, Netlify, Git submodules, GitHub Actions · Commit reference
After contacting Netlify in late 2019, a generous Open Source sponsorship was provided. This matched their Pro plan, which allowed for far greater flexibility and expanded limits.
As mentioned previously, maintaining a handful of repositories and websites was close to impossible. The decision was taken to move the website infrastructure to its own repository (30-seconds-web) and use Git submodules to fetch the content repositories at build time.
As GitHub Actions were available at the time, it was decided that deployment to Netlify would be triggered via pushing to the production branch. This would ultimately result in the deployment action adding an empty commit to the branch and forcing it to update. Netlify would then trigger a build from the branch and deploy the website.
Git submodules were tricky to work with, but the main advantage was that updating them to the latest commit for each repository was trivial via a script that could run on Netlify.
git submodule update --recursive --remoteWhat this meant was that Netlify could always fetch the latest commits from all repositories and the web infrastructure repository didn't need to do anything. Additionally, development was also pretty simple and fetching the latest snippet data was only a few keystrokes away.
In order to make this work correctly, a prebuild command was added to package.json, performing the content update whenever npm build (the Gatsby build) was run.
Some additional utilities and functionality were introduced at this time, such as a pretty decent search engine, a snippet ranker, a recommendation system etc. Some of these tools are still in use today with minor changes to their code.
Trivia
Some new JS tools were introduced at this time, such as Storybook and Jest for testing, alongside a handful of configuration files and utilities.
Most of the structure and logic of the original setup for the web infrastructure holds true to this day. Due to framework changes, a lot of the current conventions are a jumble of different conventions, picked up from each new tool along the way.
Until the next milestone (pun intended), numerous releases and changes were made to the web infrastructure. These are far too many and complex to talk about and some of the details have faded in my memory. Feel free to browse 30-seconds-web releases prior to v.6.0.0 to check them out for yourself.
During this stage, I had to make some DNS changes, if I remember correctly. To cut a long story short, I made a mistake that meant the website would never go live on my domain. After a very long chat with Namecheap's support, I realized that it wasn't a propagation issue that kept my website down for half a day, but a typo on my part.
Replacing Gatsby with Next.js Early 2021
Tools: Next.js, React, Netlify, Git submodules, GitHub Actions · Commit reference
Gatsby's issues kept on piling up for quite a while. From the inability to upgrade between minor versions to slow GraphQL queries and tons of bugs and broken builds, I sought a better tool for quite some time. Next.js was quite popular at the time and was also based on top of React and supported by Vercel. It seemed like a match made in heaven.
The core philosophy was still almost the same - Git submodules for content fetched by Netlify for Next.js deployments triggered via the production branch scheduled or triggered via a GitHub Action.
Deployments were now triggered by pushes to the master branch or by a cron job scheduled daily. Additionally, a manual trigger was provided to allow for emergency deploys to be made without the need of an extra commit added to .
The extraction process used at the time might be worth explaining briefly, as it ties into the next milestone. A handful of parsers were created to read and parse files, converting Markdown and plaintext into JSON objects. The result of parsing these content files was ultimately some instances of a few JS classes, representing the main entities of the project. Some concepts such as adapters and decorators were borrowed from other frameworks. Finally, serializers would take the enriched data and produce pages for each snippet, along with listings and collections.
On a side note, there's a huge list of redirects that is used by Netlify. This is pretty important for SEO, as it redirects old URLs to new ones. There are lots of artifacts in there, such as language renames, listing sorting removals, snippet renames etc. Take a look for a slice of the project's history.
Another interesting addition is the Twitter bot workflow. This GitHub Action used a JSON file that was randomized each deployment, generated by a script called chirp. This allowed only specific snippets to be eligible and made their information available to the bot that run at specific times to post on Twitter. In order to post to Twitter, certain secret environment variables were added to the repository for the Twitter API and Unsplash API.
Finally, the website's sitemap was generated manually via a script. While Next.js (and possibly Gatsby) provide convenient options to do this, not being bound to the framework except for page generation would prove to be a wise decision.
Custom ORM-like solution Early 2022
Tools: Next.js, React, jsiqle, Netlify, Git submodules, GitHub Action · Commit reference
While the infrastructure was mostly stable, the build process was slow and clunky. Inspired by Object-Relational Mappings (ORMs) (mainly Ruby on Rails' ActiveRecord), I developed jsiqle, which somewhat resembled an ORM approach to what I was previously doing with Object-Oriented Programming (OOP).
While the tool's codebase is very complicated (almost unnecessarily so), the principles are simple. Models are used to model individual entities with specific attributes. These models can then define relationships, methods and derived properties and are grouped together in a schema. Serializers are provided to turn these models into plain JS objects that can be written into JSON files.
Using jsiqle removed a lot of the complexity from the website repository, allowing for simple models and relationship definitions, as well as very clean serializers. An extraction process handles parsing Markdown files into a large JSON (stored temporarily in .content/conten.json) that is then used by the application entry point to populate the in-memory dataset. These two steps in the preparation process could be run separately and the output of the first one could also be easily inspected.
A final step is performed via the use of serializers and writers, storing all content in individual JSON files inside a directory. This is then used by Next.js's helpers to get the paths of each page to generate.
The application entry point in itself contains a lot of magic and complicated code to make development easier and faster. Models, serializers and utilities, for example, are loaded automatically and exposed globally via the Application object, allowing them to be accessed anywhere.
One of the conveniences afforded by jsiqle is the ability to have an interactive Node.js terminal (via the npm console command). This allows developers to query the extracted dataset, much like they can do in the Ruby on Rails console.
Trivia
jsiqle originally stood for JavaScript In-memory Query Language with Events. The "with Events" part is no longer true, though.
Due to the way a lot of the website works, Google Analytics were only loaded if the user has accepted cookies. Some complex and quirky code had been added to send those initial page views, when appropriate.
If you look at the document template for Next.js, you will notice this line (<script>0</script>). To this day, I have no clue why this worked or who suggested it, but it fixed some really obscure server-side rendering (SSR) problem or something similar.
Replacing Next.js with Astro Early 2023
Tools: Astro, jsiqle, Netlify, Git submodules, GitHub Actions · Commit reference
Next.js was starting to get in the way more so than anything else. React was also not necessary in most cases, slowing down the page. A return to static site generation (SSG) was justified, and Astro was the perfect tool, combining flexibility and simplicity.
The migration from Next.js was surprisingly easy and adapting React components to Astro ones didn't take nearly as long as I expected. This resulted in a very similar repository structure and build process as the one before it.
A point of relative interest at this stage would be the omni search component, built using the Web Components standard, allowing for a very dynamic component written in plain JS, without React or other similar frameworks.
As Astro is essentially a templating language with a static site generator built-in, builds were now running faster and the website's performance and SEO were improved.
Speaking of performance, both Astro and Next.js provide some asset optimization tooling, yet I chose to roll up my own. Assets are loaded from a content repository and are then processed via sharp, producing lower resolution images ready for the web, as well as images in the WebP format. These are then stored in a temporary directory (.assets) and used for development, without being regenerated every time the predev command runs (already generated assets are skipped). If the build is in a production environment (Netlify), the assets are also copied to the public directory to be served as part of the website.
Trivia
Initially, I was looking into Svelte and SvelteKit as a replacement for React and Next.js. I stumbled upon Astro by accident and decided to give it a quick try. Surprisingly, I liked it far more than I expected and, due to a very simple migration process, I opted to use it.
Content repository merge Mid 2023
Tools: Astro, jsiqle, Netlify, Git submodules, GitHub Actions · Commit reference
As community contributions were way lower, maintaining a handful of content repositories wasn't justified. Additionally, structural changes to how Markdown files were written required committing changes to 5 different repositories - a problem I was all too familiar with.
Merging all content repositories sounded exciting, yet I had no idea how to do so or if it was even possible. In fact, I thought it wasn't possible at all. Luckily, I found an article online outlining the process. What it all boils down is essentially adding the other repository as a remote locally and merging its master branch into the current master branch with the --allow-unrelated-histories flag.
git merge swtest/gh-pages --allow-unrelated-historiesAfter that point, the website repository's submodules were removed and only one was left in order to allow for the automated content fetching on Netlify to continue as it always did.
Trivia
Merging unrelated histories makes it really hard to follow changes and makes the git tree almost unusable. It also makes issue and pull request references reference the new repository instead of the original one. Don't do it, kids, it's not worth it most of the time.
Website & content repository merge Late 2023
Tools: Astro, jsiqle, Netlify, GitHub Actions · Commit reference
After all the changes back and forth, it was starting to become clear that the separation between website infrastructure and content made little to no sense. The unrelated histories merge strategy was used once again, retaining the exact structure of the website repository. The content directory, previously containing git submodule references, now contained the raw content files.
This change created some new challenges, such as builds only triggering for website code changes and not for content changes. Accomplishing this required a simple revamp of the GitHub Actions I had created.
The scheduled and manual deploy workflows were merged into one. This workflow triggers as needed via cron or manual action and will always trigger a deployment to Netlify via a commit in the production branch. The push-based workflow, however makes use of the Paths Changes Filter, along with some rules to only trigger deployments if any of the website-related files have changed. This prevents minor changes in content files triggering a deployment. Finally, both workflows now output the triggering event as part of the commit message, so that it's easier to track what caused a deployment.
Another point of interest is the changes that I had to make to the Netlify configuration. I was very skeptical if this would require a lot of manual work, but luckily I could change the repository I deploy from via the platform's UI. This only took a couple of minutes, caused no downtime for the website and needed no DNS changes, as everything was already configured.
Trivia
I was at the time working on some new GitHub Actions that would allow for pull request QA checking, making content curation much simpler. These would be canned later, as community contributions were put on the back burner.
Towards the end of this period, I transferred the 30-seconds-of-code repository back to my personal account. Links to the previous URL will link to the new one, as long as the organization still exists on GitHub, which is the reason why I'm not deleting it.
Quality of life improvements Late 2023
Tools: Astro, jsiqle, Netlify, GitHub Actions · Commit reference
During the last few months of 2023, a lot of quality of life changes were made to the website, affecting end users (UI changes), authors (content creation tooling), infrastructure (content parsing and building) and development (hot reloading and content queries).
Some changes that were made include, but are not limited to, an updated MarkDown parser, the addition of automated CodePen embeds, GitHub-style admonitions, watch mode for content hot reloading, and prepared queries for the console.
Additionally, the codebase was migrated to ESM, while a rewrite and restructure was done to ensure everything worked as intended. Finally, some unnecessary integrations (Google Ads, Google Analytics) and infrastructure parts (icon font, second content font, multiple format images, executable snippet code) were removed from the codebase altogether.
Trivia
Some of the remark plugins were (and still are) written in a very trial-and-error sort of way, where I try to figure out what nodes are produced by the parser and what I can do to transform them. I often resort to adding raw HTML into the output, which is not ideal and could definitely be improved.
CodePen embeds needed some styling, as to not clash with the website color palette. The documentation for embedding and using them is scattered around, outdated and often incorrect. I had to delve into code and tinker a bit with them to get it right.
Watch mode was implemented in an arbitrary fashion, making it slow and a little fiddly. However, it provided enough utility to keep around until later iterations, where I could make it more elegant and practical for my needs.
Rewrite in Ruby on Rails Mid 2024
Tools: Astro, Ruby on Rails, SQLite, Netlify, GitHub Actions · Commit reference
After about 6 months of working on cleaning up and consolidating content, I decided to try an infrastructure change, namely rewriting all of the backend code in Ruby on Rails. The idea was to create a JS - Ruby on Rails - JS (Astro) sandwich.
The first JS part would be responsible for parsing MarkDown and highlighting content, much like before, so I wouldn't have to replace this part of the infrastructure, which worked well. The output of this step would be a JSON file, same as before. Some optimizations were made to parallelize code execution and separate this logic into what was essentially a standalone module that could run, process input, finish and exit.
Ruby on Rails would then replace the jsiqle part, using its ActiveRecord and SQLite to load the JSON data into a persistent storage. The SQLite storage would be treated as a throwaway layer, meaning it would be regenerated on application restart. This allowed the code to interface nicely with the latest data and easily use ActiveRecord helpers.
Finally, rake tasks and serializers were used to output JSON data in the same structure as before to then feed into Astro.
Trivia
While this approach made it a lot easier to query data and optimize structures, it was relatively slow, mainly due to the I/O overhead needed for the additional SQLite step.
Build times with Ruby on Rails were about 3x slower than JS prior to them and about 4x slower than the optimized JS code that shipped after the next refactor.
Migrating from JS to Ruby on Rails provided a great opportunity to separate concerns and build steps into the initial parsing (JS), data processing for serialization (Ruby on Rails) and Static Site Generation (Astro). This allowed for further refactors, where these three steps are separate.
Due to the way the refactored codebase was set up, I never managed to crack the code of how to add a watch mode. This, however, wasn't a problem, as this setup was really short-lived.
Rewrite back in plain JS Late 2024
Tools: Astro, Netlify, GitHub Actions · Commit reference
Seeing that Ruby on Rails was slow for my use case, I started migrating the Ruby code back to JS. The Ruby on Rails directory structure provided a great way of organizing files, logic and figuring out what goes where, so I kept most of the conventions and structures intact.
Porting from Ruby back to JS took about a week, right before the summer break, and it was not particularly hard, as I was relatively fluent in both languages. The performance improved quite a bit, even compared to the setup I had prior to the Ruby on Rails rewrite.
A notable update at this time is the custom model system I developed, inspired by the previous ActiveRecord implementation, which is thoroughly documented in the Modeling complex JavaScript objects series of articles, written in late 2024. This system essentially replaced jsiqle, which was far too slow due to the usage of Proxy and being agnostic, while a custom-tailored built-in solution was way faster. The performance increase was only compounded by the use of new JS features (e.g. static initialization blocks) that made the code more robust, less verbose and significantly more customizable.
I kept working on small changes, while revising content, for a few months, most notably adding a better watch mode. The new watch mode watches all files under the content directory for changes. When a change is detected, the JSON data needed for Astro is recalculated on the fly, by rerunning the entire processing pipeline, skipping assets and other things that aren't important to improve response times.
Trivia
Notifying Astro about changes in content was tricky. To circumvent the issue, I devised a trick where a timestamp.js file is generated at the end of each content rebuild. This is then imported into Astro, so that it notices a dependency change and it will reload the page.
The new watch mode delivered a near-instant editing experience, where switching from VS Code to Chrome (literally under a second) would be enough to rebuild the content and trigger an Astro page refresh, on a MacBook Air M1.
During this time, the Update Logs functionality and announcement on the home page was introduced, allowing for a more personal touch.
While the 2-step content preparation (MarkDown parsing, serialization from models) was still made up of distinct, decoupled steps, the watch mode didn't make use of this separation. To this day, the separation of the layers is intact, yet only needed as a means to debug parts of the pipeline more easily during development of new features.
In late December, I redesigned my personal portfolio website to match the style and color palette of 30 seconds of code, as part of the greater strategic shift in the brand narrative (30 seconds of code now being a personal blog and all that) to feel more concise and like part of the website itself. Fun fact: My personal portfolio website also doubles as a printable CV, with custom print styles to be easily exported to PDF.
Functionality extensions Early 2025
Tools: Web Components, Astro, Cloudflare, GitHub Actions · Commit reference
In the months that followed, I focused more on content rather than infrastructure. However, as I focused on larger projects, I also liked tackling more challenging tasks, which also lead to some infrastructure projects that really upgraded the website.
One such project was the complete search overhaul, as the previous search system dated back to early 2020 and was severely underpowered for the now long-form content and the the sheer amount of articles it had to serve. I developed a new Natural Language Processing (NLP) solution with TF-IDF for this purpose, building a document index, inspired by ElasticSearch, which was later enhanced with partial matching via n-grams. The new search journey is documented in this series of articles, written at around the same time as the implementation.
Another major overhaul was the removal of PrismJS in favor of Shiki for code highlights. This provided better grammars and syntax highlighting, allowing for more accurate, VS Code-like highlights. It also unlocked many new features, such as bracket pair colorization, line highlights, collapsible code blocks etc.
Moreover, another addition was support for Web Components to enhance the content on various pages. These include, but are not limited to, the table of contents scroll spy, code tabs, dynamic LaTex support, Baseline support and step visualization. All components are loaded dynamically, as needed, alongside their CSS via ECMAScript Modules (ESM), while having accessible fallbacks, in case anything goes wrong. Apart from these, I also developed a way to link to different articles and collections via custom MarkDown syntax, allowing for more interesting links between articles.
Finally, I moved the website to Cloudflare Pages for hosting, as it provided a better experience and more features than Netlify (namely integrated Web Analytics and AI Bot protection). The website is now served from Cloudflare's edge network, which provides faster load times and better performance. Their bandwidth and usage limits are pretty generous, so I don't expect to run into any issues with the current traffic any time soon. The whole migration process took just one evening, with most of the issues I faced being related to DNS setup that I'm not particularly good at.
Trivia
I plan to enhance the NLP setup to later introduce topic modeling, embeddings and dynamic personalized recommendations, based on the current user session.
The search system still runs on the client, using a serialized JSON. I kept the original setup mostly intact, however I had to find a good balance between quickly setting it up on the client and minimum network load. I settled a frequency mapping system that makes it easier to reconstruct the document index on the client, without having to serialize huge loads of data for it (e.g. i:2 know:2 that nothing).
Shiki's codebase is a little tricky to browse and the documentation wasn't very accessible for my needs. I ended up reading most of the codebase to make sense of it. I also didn't like how some official transformer plugins were implemented, so I rolled up my own whenever possible (example).
I wanted code tabs to work in the most frictionless way possible. This meant that I had to use a details element to wrap them, which made me resort to some pretty clever and complicated hacks to make them work. A simplified implementation is documented here.
LaTex libraries are extremely heavy, so build times, especially in watch mode are terrible. It's not optimal to load the library on the client side, but it only happens in the few (~10) articles that need it and is also cached by the browser.
Remember how I removed PrismJS? Well, I lied... a little! Watch mode still uses it in the form of a Web Component for faster edit time page refreshes. There is now a watch mode and an observe mode, using PrismJS and Shiki respectively.
I updated Sass to the latest version at this time and I gotta say their automatic migration tools and migration guidelines are a breeze to follow.
A lot of these later updates are documented in the website's Update Logs.
Phew! That was a ton of information, especially considering it's almost 8 years worth of work, mistakes, learnings and experiments. I hope you found this article interesting and that you'll take away some useful insights that you can apply to your own projects. See you in the next one! 👋
Snippets Articles S Http Status Codes Cheatsheet (`content/snippets/articles/s/http-status-codes-cheatsheet.md`)
title: Common HTTP status codes Cheat Sheet shortTitle: HTTP status codes tags: [webdev,http,cheatsheet] cover: hiking-walking excerpt: Familiarize yourself with the most common HTTP status codes with this handy cheatsheet. listed: true dateModified: 2021-06-12
1xx - Informational
- 100: Continue - Everything is ok so far
- 102: Processing - Request is being processed, no response available yet
2xx - Success
- 200: OK - Request successful
- 201: Created - Request fulfilled, new resource has been created
3xx - Redirection
- 301: Moved Permanently - Resource permanently moved to a new URL
- 302: Moved Temporarily - Resource temporarily moved to a new URL
4xx - Client Error
- 400: Bad Request - Server cannot understand and process the request
- 401: Unauthorized - Authentication required, user not yet authenticated
- 403: Forbidden - Insufficient access permissions to the resource
- 404: Not Found - Requested resource not found
- 410: Gone - Request no longer available due to intentional removal
5xx - Server Error
- 500: Internal Server Error - Generic unhandled server error
- 502: Bad Gateway - Gateway server got an invalid response
- 503: Service Unavailable - Server temporarily unable to handle request
- 504: Gateway Timeout - Gateway server didn't get a response in time
Snippets Articles S Learnings From Vibe Coding A Small Web Project (`content/snippets/articles/s/learnings-from-vibe-coding-a-small-web-project.md`)
title: What I learned from vibe coding a small web project with GitHub Copilot and GPT-5 mini shortTitle: Vibe coding learnings tags: [webdev,programming] cover: interior-19 excerpt: A short experiment to see what vibe coding feels like and how far AI can take a project without writing any code myself. listed: true dateModified: 2025-10-24
A few weeks ago, I decided to finally try vibe coding, mostly out of curiosity, but also due to necessity and a lack of time. The bet was to create a small web project using a coding assistant, without much manual coding.
Setup
First things first, the tools I used for the job are what I'm most familiar with - GitHub Copilot and VS Code's Agent Mode. While I use GPT-4.1 as my daily driver (what a time to be alive, I'm calling an LLM my daily driver), I decided to give GPT-5 mini a go. Spoiler alert: both models seem to be on par, so we won't be focusing on that too much.
I'm not going to dive into the project details, but rather share some of the learnings and insights I gathered from this experience. All you need to know is the tech stack, which includes Go on the backend, SQLite for the database, and React with Redux and Sass on the frontend.
I should also mention that I'm familiar with all of these technologies, but I'm really a beginner in Go, so I could use the help in setting up the project structure and gluing things together.
The good
Starting out with the good, the experience was surprisingly smooth. My little AI assistant was delightfully quick in setting up the project structure and installing the dependencies I wanted, without me having to lift a finger. Full disclaimer that I knew most of the toolkit I wanted to use, so it didn't have to guess much in the earlier stages.
I was pleasantly surprised by how well it handled boilerplate setup and small refactors I suggested. It was also very good at performing basic tests via curl commands and stuff like that to verify that the endpoints were working as expected.
Another thing I noticed is that I didn't need to dive too deep into the documentation of that many tools. As I'm sort of familiar with Go's ecosystem, I managed to find some dependencies that looked right. I didn't want to spend 40 hours debugging a live reloader and writing my database connection call, so I just asked the AI to do it for me. What would have taken me a day or two, took a couple of hours. Bonus points for not having to read through a ton of docs that I wouldn't need ever again during development of the project.
Last but not least, it was truly a learning experience. Seeing how the agent was restricted to terminal commands and the editor, I noticed how it did things I wouldn't have thought of or been used to. For example, I like how it used curl to test endpoints, or lsof to find process IDs, and actually picked up the trick myself after that!
The bad
Ok, now onwards to what I didn't like. It's really not that smart! As soon as you have enough moving parts - and that's just a half a dozen things - it starts to struggle, hallucinate, or get confused. And don't even get me started on indentation and formatting, which is entirely impossible to get right on its part. I had to manually fix it in literally every single file it generated.
One example of its limitations became quite obvious when I was debugging one of the first endpoints in the project. The database was set up, there was a table with data and I had a working connection to it from Go. I even double checked via sqlite3 on the terminal to see that everything was there. For some reason, the endpoint returned 404.
When I tried explaining it to Copilot, it insisted that the database connection was the problem, and suggested a fix that was completely off, almost erasing my database in the process. Yeah, I wasn't too happy about that!
I knew it wasn't database-related, so I prompted it a couple of times, but it kept insisting that the connection was the problem. I finally gave up and debugged it myself, realizing that the problem was actually in the live reloading tool. I told it where to focus and it sort of got it right, but not really, so I ended up patching the issue myself.
The worse
This last example brings me to my next point, which is that, sadly, it gets even worse. Not only does the AI get stuck and obsessed over issues, it won't even produce the correct fixes in some cases, update the right dependencies or even find the right documentation.
I looked up the documentation of the live reloading package I was using and, as it turns out, CoPilot installed a fairly ancient and buggy version. I updated it and tried to fix the issue, only to realize that the configuration file it produced was very broken and, by some odd miracle, in the haze of terminal commands it ran before, it produced a working build just once.
I then proceeded to check my package.json file and, lo and behold, every single dependency was outdated. And I'm not even talking like minor versions here, it was older than a year. I understand this is reasonable, since the model was trained on data that is at least a few months old, but still, it was quite frustrating to have to update everything manually.
The worst part is it did try to list the latest versions of the dependencies on npm and then it proceeded to install older ones, presumably because it only had their documentation in its training data. Still, some of the code it produced didn't even match the versions it installed, resulting in a couple of simple, yet annoying issues here and there.
I won't even go into best practices, security issues and stuff like that, because it would take a whole other article to cover that. Just be aware that you should always double check everything it produces, as if it was a distracted junior developer with access to last year's internet.
Other takeaways
Apart from having some fun and realizing how far AI has come, I also learned a few solid programming things, which is, I think, the most value I got from this experience.
Like I said before, I picked up the lsof -i :8080 -t trick to find the process ID of a server running on a specific port and kill it. I also spent some more time with tools like sqlite3 and curl, due to the fact that the AI used them or prompted me to use them here and there. And, luckily, I refreshed my knowledge of some Go concepts and Makefiles, which I hadn't used in a little while.
I also like how it got me to focus on the project structure, tools, ecosystem and the big picture. As a beginner in Go, I would have struggled much more, resulting in me frustrated and thinking the ecosystem isn't there yet. Having said that, I still believe that I should have written the project in Ruby on Rails and not Go, as I'm not yet at the level where I could tackle it and maintain it properly. Still, I'm pretty sure I would have had a different outcome and perspective had I done it without the AI's assistance.
Finally, as I had the chance to experience a stack I haven't tried before, it solidified a couple of things in my head. The oddest one is that migrations, as a database concept, aren't limited to ORMs, but rather a general concept that can be applied to SQL databases, even on a low level. Yeah, I've been using them in Rails for a while, never thought about it that way. I also noticed how one sets up Overmind and Procfiles, which I had only seen in projects I had worked on, never set them up myself.
These small nuggets of knowledge or refreshers seem like the most valuable takeaway for me and my growth as a developer.
Closing thoughts
If you are considering vibe coding, I would say give it a go, but it's quite the gamble. Be prepared for some frustration and for it to eventually become a hassle. LLMs are pretty helpful, but they definitely have a long way to go to replace a human developer, even a junior one. The best use case I could think of, writing boilerplate code, wasn't terrible, but anything more complex seems like a stretch right now.
Still, I think developers, especially junior and mid-level ones stand to gain a lot from watching the AI code and fill gaps in their knowledge. So, yeah, give it a go, but don't expect too much!
Snippets Articles S Markdown Cheatsheet (`content/snippets/articles/s/markdown-cheatsheet.md`)
title: Markdown Cheatsheet tags: [webdev,documentation,cheatsheet] cover: typography excerpt: Markdown is the most popular markup language for writing documentation. Here is a cheatsheet with the most common syntax. listed: true dateModified: 2025-06-08
Markdown is a lightweight markup language with plain text formatting syntax, easily convertible to various output formats, such as HTML. In the programming world, it's widely used to format documentation, README files, GitHub issues, pull requests and comments. While there are various flavors of Markdown, the basic syntax is shared across implementations.
[!TIP]
As most users will be using GitHub Flavored Markdown (GFM), a more complete reference can be found here. You may also want to check out Markdown Guide for a more comprehensive overview of Markdown syntax.
Headings
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6Paragraphs
This is a paragraph.
This is another paragraph.Emphasis
**Bold text** or __Bold text__
*Italic text* or _Italic text_
***Bold and italic text*** or ___Bold and italic text___Lists
- Unordered list item 1
- Unordered list item 2
- Nested unordered list item
- Unordered list item 3
* Unordered list item 1
* Unordered list item 2
* Nested unordered list item
* Unordered list item 3
1. Ordered list item 1
2. Ordered list item 2
1. Nested ordered list item
3. Ordered list item 3
1. Ordered list item 1
1. Ordered list item 2
1. Nested ordered list item
1. Ordered list item 3Links
[Link text](https://example.com)
[Link text with title](https://example.com "Title")Images
Code
Inline code: `code`
```
Block code
```Blockquotes
> This is a blockquote.
>
> It can span multiple lines.Tables
| Header 1 | Header 2 |
|----------|----------|
| Row 1 | Row 1 |
| Row 2 | Row 2 |Horizontal Rule
---Snippets Articles S No Code Is Inherently Evil (`content/snippets/articles/s/no-code-is-inherently-evil.md`)
title: No code is inherently evil tags: [webdev,career,programming] cover: coffee-n-laptop-3 excerpt: No code tools? No code at all? No code you write? Prepare for a triple entendre that cuts deep into the world of software development. listed: true dateModified: 2025-08-21
No code is inherently evil. That's not a typo. It's a triple entendre, and the more time you spend building software, the more each meaning resonates with you. Let's dig in together and see what makes this idea so fascinating.
No code tools
Let's start with no code tools, an ever more-relevant topic in the age of AI. They promise to save you from the pain of writing code. Drag, drop, publish. It feels like magic, until it doesn't.
The honeymoon ends when you need to do something custom. Suddenly, you're staring at a wall of auto-generated spaghetti, or worse, a locked box with a single escape hatch that dumps you into a world of pain.
Want to add a feature? Rewrite half the project. Want to integrate with something outside the walled garden? Prepare for third-party hacks, ballooning costs, and a maintenance nightmare.
No code tools are often just a footgun: easy to pick up, but just as easy to shoot yourself in the foot with. The easy start is a trap. The real cost comes later, when you need to scale, customize, or migrate.
Analysis paralysis
But there's another kind of no code evil: writing no code at all.
The cult of best practice tells us to analyze, plan, and optimize before we even start. We chase the best framework, the right architecture, the perfect stack. We freeze, paralyzed by choice. Meanwhile, the real world moves on.
The truth? You learn more by shipping something ugly than by endlessly debating what's best. Write code, even if it's bad. Ship it, break it, learn from it, and do it better the second time.
The first version is disposable. The second is informed by reality. After all, you can't refactor what you haven't written. Writing bad code is a prerequisite to writing good code. Don't skip steps. Experiment, reflect, iterate.
Code itself
Finally, let's talk about the code itself. No code you write is evil by default. Code is just instructions. It's not malicious unless you make it so.
But here's the twist: even your best code will eventually be stretched, twisted, and abstracted into something you never intended. Features change, requirements shift, and suddenly your elegant solution is a Frankenstein's monster.
That's not evil; it's just entropy. The real evil is refusing to change, or worse, contorting your code into unrecognizable shapes to fit every edge case. Sometimes, the right move is to write more code, even if it means duplicating logic or starting over. Refactor later, when you know what actually matters.
Remember, code isn't sacred. It's a tool. Use it, break it, throw it away, and write more whenever you need to.
Conclusion
So, no code is inherently evil. No code tools can trap you, if you're not careful. Endlessly pondering over best practices and writing no code leads to stagnation. And no code you write is evil, until you let it rot. Write more code. Ship early. Refactor when it hurts. That's how you build things that last.
Snippets Articles S Tech Stack Refactoring Problems (`content/snippets/articles/s/tech-stack-refactoring-problems.md`)
title: How your tech stack is causing your refactoring problems shortTitle: Tech stack refactoring problems language: javascript tags: [webdev,career,programming] cover: laptop-hands excerpt: Infinite flexibility in your tech stack can lead to refactoring headaches and technical debt. Here's how to avoid the trap. listed: true dateModified: 2025-10-08
Ever feel like your codebase is fighting you at every turn? The tools you choose, your programming language of choice, frameworks, and overall stack, shape how you build, refactor, and maintain software. Sometimes, the very flexibility that drew you in becomes the root of your biggest headaches. Let's dig into why your tech stack might be the silent culprit behind those endless refactoring sessions and mounting technical debt.
Infinite flexibility, painful refactoring
Flexible languages like JavaScript and Python give you endless ways to solve a problem. That freedom feels empowering, but it comes with a catch. Every new feature, syntax, or method is a potential footgun. You can shoot yourself in the foot by designing APIs with too many options, clever overloads, or many ways to do the same thing. The result? Codebases that are hard to reason about and even harder to maintain.
When your codebase is a patchwork of clever tricks and alternative patterns, refactoring becomes a nightmare. You start with good intentions, but the complexity grows. Each refactor adds another layer of abstraction, another workaround, another set of edge cases. Instead of simplifying, you end up with a tangled mess. Debugging gets harder, productivity drops, and frustration rises.
Opinionated stacks force better design
Languages like Go are (in)famously opinionated. They do not let you overload functions or invent alternative syntax for the same operation. You have to pause and rethink your approach. You are forced to design explicit APIs and clear modules. It feels restrictive, but it saves you from yourself. You spend less time refactoring and more time building features that work.
Ask yourself: do you need three ways to sort an array? Do you need methods with slightly different side effects? Is infinite flexibility helping, or is it just making your code harder to understand? Most of the time, a simple, explicit API is better. It is easier to test, easier to debug, and easier to refactor when you need to.
Overengineering and technical debt
Before you add another method or another overload, stop. Rethink your API. Rethink your module boundaries. Would splitting your code into smaller, focused modules help? Would making your API more explicit reduce confusion? It is more painful now to rewrite something, but it is even more painful to patch it over and watch the technical debt pile up.
Trying to anticipate every possible future need leads to overengineered solutions. You add hooks, options, and configuration for things you might never use. As the codebase grows, complexity mounts, and soon you are crawling to a halt. Technical debt is not just a buzzword. It is the real cost of poor design decisions made in the name of flexibility.
Keep it simple, force good design
Pause. Redesign your code. Make it simple. Split it into modules if needed. Force good design decisions. Make usage explicit. Favor clear APIs over clever ones. Every time you touch your code, make a small cleanup. You do not need refactor sprints. You just need to take care of your code, one change at a time.
A phrase I really like is "eat the frog". It means doing the hard thing first. Redesign, refactor, and simplify before the debt piles up. It is better to fix the problems early than to let poor technical decisions slow you down later. Your future self will thank you.
[!NOTE]
Apparently, "eat the frog" is a time management technique, that means tackling your most challenging or dreaded task first thing in the morning. I had only heard of it on YouTube and thought it was just a metaphor. Still, the point stands: tackle the hard stuff early to avoid bigger headaches later.
Snippets Articles S Technical Debt (`content/snippets/articles/s/technical-debt.md`)
title: Technical debt tags: [webdev,career,programming,management] cover: new-york excerpt: Learn what technical debt is, its causes and symptoms, and how to deal with it. listed: true dateModified: 2022-10-09
Technical debt is a jargon term that gets thrown around a lot in the software development industry. It's worth understanding what it means, how to spot it and how to deal with it, once it inevitably rears its ugly head.
Definition
The term itself comes from a metaphor, in which the team's lack of understanding about the codebase leads to continuous disagreement. Continuously stumbling upon this situation slows down progress, feeling like paying interest on a loan.
Technical debt is effectively the natural result of writing code about something we don't have a proper understanding of.
Causes
But what causes technical debt in the first place? As hinted at by the previous definition, it arises from a disagreement between business needs and how the software was written. The root cause for such disagreements can either appear early on or later down the line.
In the former case, a lack of understanding might have lead to laying the foundations incorrectly, therefore carrying the debt from the start. Poor design choices, lack of foresight and a lack of communication between the team members can all lead to this.
In the latter case, the debt might have been incurred due to a change in the business requirements, which the software was not designed to handle. This is often the case with projects that move rapidly, making it hard to adapt to the changing needs.
Interestingly enough, poor code and not following best practices only account for a portion of technical debt. This means that, while following best practices and writing clean code can help reduce the debt, they are usually not the main cause.
Symptoms
So, how do you know if you have accumulated technical debt? It's not that hard to spot most of the time. Lowered productivity is usually one of the first signs. This translates to increased development time or effort estimates that are not met. This makes sense intuitively, as maintenance costs mount up and the codebase is not as easy to work with as it should be.
Following lowered productivity, teams with accumulated technical debt might start to notice decreased code quality. When estimates are not met, developers might be tempted to cut corners and write code that is not as clean as it should be. This is a natural reaction to the pressure of meeting deadlines, but it can have a negative impact on the codebase in the long run.
Finally, this spirals into a vicious cycle, as the codebase becomes harder to work with, which leads to more pressure, lowered morale and even more technical debt. This is why it's important to spot the symptoms early on and take action to reduce it.
Solutions
Dealing with technical debt is not always easy, but it's not impossible either. The most important step is realizing that it exists and that it affects the team's productivity. Once that is done, the team can start to take action. Solutions vary, depending on the team, industry and nature of the technical debt itself.
Broadly speaking, it's acceptable to take on debt early on to get the project off the ground. Identifying and keeping track of it ensures that the team knows where to start when the time comes to start dealing with it. This also helps figure out how much of an impact it has and how to prioritize maintenance tasks.
As soon as the project is out of the door, the team should start to reduce debt or at least not take on any more. Prioritization can be based either on the impact or the effort required for each refactor. A combination of two can also be beneficial, as it can allow small maintenance tasks to fill in the gaps, while more impactful refactors are planned and coordinated accordingly.
After getting to a manageable level, it's important to keep technical debt in check. Here's where having a process in place can work wonders. By knowing how to keep debt in check, when to take action and continuously working towards that goal, the team can ensure that the codebase is always in a good state.
Conclusion
Technical debt is a natural, unavoidable part of software development. Miscommunication and lack of understanding can lead to it, resulting in lowered velocity and morale. Luckily, a bit of awareness, good communication and an actionable process can help reduce it and keep it in check.
Snippets Articles S Terminal Shortcuts Cheatsheet (`content/snippets/articles/s/terminal-shortcuts-cheatsheet.md`)
title: Bash Shortcuts Cheat Sheet tags: [webdev,bash,cheatsheet] cover: terminal excerpt: Maximize your productivity when working with a bash or other shell terminal by using these keyboard shortcuts. listed: true dateModified: 2021-06-12
- Ctrl + C Terminate the command
- Ctrl + Z Suspend the command
- Ctrl + A Move to the start of the line
- Ctrl + E Move to the end of the line
- Ctrl + F Move forward one character
- Ctrl + B Move backward one character
- Ctrl + U Delete from the cursor to the start of the line
- Ctrl + K Delete from the cursor to the end of the line
- Ctrl + W Delete from the cursor to the start of the word
- Ctrl + D Delete the character under the cursor
- Ctrl + H Delete the character before the cursor
- Ctrl + T Swap the character under the cursor with the previous one
- Ctrl + L Clear the screen
Snippets Articles S The Frontend Trap (`content/snippets/articles/s/the-frontend-trap.md`)
title: The frontend trap tags: [webdev,career,programming,jobs] cover: coffee-n-laptop-2 excerpt: How staying on the frontend can limit your career growth and a hot take on how to escape it. listed: true dateModified: 2025-08-19
A while back, I was having a conversation with a mid-senior colleague I'm mentoring about their career. In the course of our discussion, my decision to expand my skillset from frontend to full-stack development came up. They were surprised and asked me why I would do that, given that frontend is fun and I'm pretty good at it, having more experience than on the backend. What I couldn't elaborate a couple of years back, when I made said decision, I could now explain in detail.
The limits of frontend work
Starting with something obvious to me, I mentioned that some high-level problems simply don't exist on the frontend. You rarely worry about distributed systems, data consistency, or scaling a service to millions of users. Most frontend work revolves around user interfaces, state management, and making things look and feel right. That's important, but it's only one piece of the puzzle.
Frontend development time is often spent handling edge cases, wrestling with browser quirks, or trying to turn every problem into a React-shaped nail. You might spend hours fiddling with CSS, chasing pixel perfection, or patching compatibility issues so users can access your app on every device. It's a grind, and it's easy to get stuck in a loop of solving the same types of problems over and over.
How the backend is different
Backend development throws you into the deep end. You face scaling, performance bottlenecks, design patterns, and architecture decisions that shape entire systems. Optimizing algorithms, picking the right data structures, and making calls that impact reliability and speed are daily challenges. The stakes are higher, and the problems are broader.
You also get hands-on with the technologies that power modern web applications. Databases, containers, message queues don't seem like buzzwords anymore. They're the backbone of most serious tech stacks. Knowing how they work makes you valuable in almost any job.
All the deep computer science concepts you learned in college finally come into play. On the backend, you'll actually use algorithms, data structures, and design patterns that might have seemed academic before. Concepts like concurrency, caching, and distributed algorithms become part of your toolkit. This is a stark contrast to frontend work, where you can go years without touching any of that.
Growing beyond the frontend
If you want to become a truly senior developer, with advanced knowledge and a deep understanding of systems, patterns, and code, you need to invest in backend development after you've mastered the frontend. That's where you'll find the challenges that push you to grow.
A more varied skillset, what we often call a T-shaped developer profile, makes you more valuable to companies. You can adapt to different situations, jump into new projects, and handle whatever comes your way. You're not just the React person or the CSS wizard. You're someone who can see the big picture and contribute at every level.
Stay curious, stay relevant
In conclusion, what was my answer? That it's better to diversify your skills than to go super deep into just frontend development. The landscape changes fast. What's relevant today might be legacy knowledge tomorrow. Remember CSS floats? IE compatibility hacks? jQuery? Ok, that last one might still be a little relevant. But the rest? They were essential once, but now they're mostly footnotes nowadays.
It's easy to stagnate by doing the same thing for years, even if you become a master at it. Normalize good enough and stay curious. Try new things, join more conversations, and adapt as technologies shift. That's how you stay relevant, energized, and ready for whatever comes next.
Snippets Articles S Virtues Of Pair Programming (`content/snippets/articles/s/virtues-of-pair-programming.md`)
title: The virtues of pair programming tags: [webdev,career,programming,jobs] cover: coffee-table-setup excerpt: Pair programming can be valuable for junior and senior developers alike, fostering collaboration and knowledge sharing. listed: true dateModified: 2025-09-20
I've got to admit, we did not expect pair programming to reshape the way we work initially. But, after months of trying it with my team, the impact is obvious.
Tackling difficult problems feels less daunting. We ask for help earlier, not as a last resort. The way we analyze and explain things is sharper, clearer. Each session brings a new perspective, and sometimes, a breakthrough.
If you're a non-believer, like I was, let me share some insights!
Mindset alignment
For senior engineers, pair programming is a window into the beginner's mindset. You see what trips up junior developers, understand their needs, their questions, and the gaps in their knowledge. This makes it easier to mentor, guide, and build trust. Sometimes, you rediscover the basics and realize how much you take for granted.
Junior engineers benefit even more. Growth happens fast when you share the process of implementation, debugging, and coding. You learn not just what to do, but why. You see how others think, how they break down problems, and how they recover from mistakes. Every session is a lesson in real-world development.
Growing together
For junior developers, pair programming is like rubber duck debugging, except the duck talks back. You get feedback, suggestions, and new ways to solve the issue at hand. It's a conversation, not a monologue. You are never alone with a problem.
Senior developers find opportunities to dive deeper into interesting topics. You get to share your wisdom, shape the mentality of new team members, and keep your own skills sharp. Sometimes, you learn something new from a fresh perspective.
Team dynamics
The benefits go beyond code. Pair programming builds strong interpersonal relationships. It strengthens team bonds and creates a sense of shared responsibility. When two people work on a problem, understanding is shared. This makes future collaboration and maintenance easier.
Pair programming creates a safe space. The simplest questions and personal difficulties can be resolved without judgment. You get practical examples, not just theory. It is easier to admit when you are stuck, and easier to ask for help. The team learns that asking for help early is not a waste of time. It leads to better outcomes for everyone.
Cross-team or cross-function pair programming is even more powerful. It minimizes blind spots, demystifies black boxes, and promotes knowledge sharing across teams. You learn how other teams work, and you bring back new ideas.
Closing thoughts
Some worry that pair programming takes twice the time. In reality, it is often faster than having someone struggle alone, then ask for help, and repeat the cycle. Brainstorming and working as a team can be more efficient. Sometimes, a tough problem is the perfect excuse for more than two people to jump on a call and solve it together.
Pair programming is not just a technique. It is a mindset. It's about learning, sharing, and building better teams. You should try it sometime. You might be surprised by what you discover!
Snippets Css S 10 Css Background Patterns (`content/snippets/css/s/10-css-background-patterns.md`)
title: 10 beautiful CSS background patterns shortTitle: CSS background patterns language: css tags: [visual,cheatsheet] cover: armchair-design excerpt: I hand picked 10 of my favorite CSS background patterns from MagicPattern for your next project. Get them now! listed: true dateModified: 2024-05-09
MagicPattern has an amazing collection of beautiful pure CSS background pattern to spice up your designs. I highly recommend checking out the full collection. Meantime, here are my top picks to get you started:
Stripes pattern
.lines-2 {
background-color: #fefefe;
background-image: linear-gradient(
to right,
#5394fd,
#5394fd 12px,
#fefefe 12px,
#fefefe
);
background-size: 24px 100%;
}Zig zag pattern
.zigzag {
background-color: #fefefe;
background-image: linear-gradient(135deg, #5394fd 25%, transparent 25%),
linear-gradient(225deg, #5394fd 25%, transparent 25%),
linear-gradient(45deg, #5394fd 25%, transparent 25%),
linear-gradient(315deg, #5394fd 25%, #fefefe 25%);
background-position:
24px 0,
24px 0,
0 0,
0 0;
background-size: 48px 48px;
background-repeat: repeat;
}Polka dot pattern
.polka {
background-color: #fefefe;
background-image: radial-gradient(#5394fd 1.2px, #fefefe 1.2px);
background-size: 24px 24px;
}Checkerboard pattern
.rectangles {
background-color: #fefefe;
background-image: repeating-linear-gradient(
45deg,
#5394fd 25%,
transparent 25%,
transparent 75%,
#5394fd 75%,
#5394fd
),
repeating-linear-gradient(
45deg,
#5394fd 25%,
#fefefe 25%,
#fefefe 75%,
#5394fd 75%,
#5394fd
);
background-position:
0 0,
24px 24px;
background-size: 48px 48px;
}Rhombus pattern
.rhombus {
background-color: #fefefe;
background-image: linear-gradient(135deg, #5394fd 25%, transparent 25%),
linear-gradient(225deg, #5394fd 25%, transparent 25%),
linear-gradient(45deg, #5394fd 25%, transparent 25%),
linear-gradient(315deg, #5394fd 25%, #fefefe 25%);
background-position:
24px 0,
24px 0,
0 0,
0 0;
background-size: 24px 24px;
background-repeat: repeat;
}Paper pattern
.paper {
background-color: #fefefe;
background-image: linear-gradient(#5394fd 4.8px, transparent 4.8px),
linear-gradient(90deg, #5394fd 4.8px, transparent 4.8px),
linear-gradient(#5394fd 2.4px, transparent 2.4px),
linear-gradient(90deg, #5394fd 2.4px, #fefefe 2.4px);
background-size:
120px 120px,
120px 120px,
24px 24px,
24px 24px;
background-position:
-4.8px -4.8px,
-4.8px -4.8px,
-2.4px -2.4px,
-2.4px -2.4px;
}Diagonal pattern
.diagonal {
background-color: #fefefe;
background: repeating-linear-gradient(
45deg,
#5394fd,
#5394fd 12px,
#fefefe 12px,
#fefefe 60px
);
}Triangles pattern
.triangle {
background-color: #fefefe;
background-image: linear-gradient(45deg, #5394fd 50%, #fefefe 50%);
background-size: 24px 24px;
}Waves pattern
.wavy {
background-color: #fefefe;
background-image: repeating-radial-gradient(
circle at 0 0,
transparent 0,
#fefefe 24px
),
repeating-linear-gradient(#5394fd55, #5394fd);
}Isometric pattern
.isometric {
background-color: #fefefe;
background-image: linear-gradient(
30deg,
#5394fd 12%,
transparent 12.5%,
transparent 87%,
#5394fd 87.5%,
#5394fd
),
linear-gradient(
150deg,
#5394fd 12%,
transparent 12.5%,
transparent 87%,
#5394fd 87.5%,
#5394fd
),
linear-gradient(
30deg,
#5394fd 12%,
transparent 12.5%,
transparent 87%,
#5394fd 87.5%,
#5394fd
),
linear-gradient(
150deg,
#5394fd 12%,
transparent 12.5%,
transparent 87%,
#5394fd 87.5%,
#5394fd
),
linear-gradient(
60deg,
#5394fd77 25%,
transparent 25.5%,
transparent 75%,
#5394fd77 75%,
#5394fd77
),
linear-gradient(
60deg,
#5394fd77 25%,
transparent 25.5%,
transparent 75%,
#5394fd77 75%,
#5394fd77
);
background-size: 48px 84px;
background-position:
0 0,
0 0,
24px 42px,
24px 42px,
0 0,
24px 42px;
}Snippets Css S 25 Css Gradients (`content/snippets/css/s/25-css-gradients.md`)
title: 25 CSS gradients for your next project shortTitle: CSS gradients language: css tags: [visual,cheatsheet] cover: colors-mural excerpt: I hand picked 25 of my favorite CSS gradients from uiGradients for your next design. Get them now! listed: true dateModified: 2024-01-12
uiGradients 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 my top picks in case you're looking for some color:
.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);
}.argon {
background: linear-gradient(to right, #03001e, #7303c0, #ec38bc, #fdeff9);
}.celestial {
background: linear-gradient(to right, #c33764, #1d2671);
}.relay {
background: linear-gradient(to right, #3a1c71, #d76d77, #ffaf7b);
}.crystal-clear {
background: linear-gradient(to right, #159957, #155799);
}.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);
}.jshine {
background: linear-gradient(to right, #12c2e9, #c471ed, #f64f59);
}.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);
}Snippets Css S Alternating Text (`content/snippets/css/s/alternating-text.md`)
title: Alternating text animations language: css tags: [animation] cover: italian-horizon excerpt: Ever wondered how those alternating text animations work? Here's a simple way to create one using CSS and JavaScript. listed: true dateModified: 2024-09-12
Alternating text animations are a great way to add some playfulness to your website. They can be used to display different words or phrases in a loop, often used to showcase a unique selling point or a list of features.
Luckily, with a little CSS and JavaScript, you can create your own. Starting with the CSS, you need an element to display the content and a simple animation to make the text disappear.
Then, in JavaScript, you can define an array of the different words or phrases you want to alternate between and use the first one to initialize the content. By using EventTarget.addEventListener() to listen for the 'animationiteration' event, you can update the content of the element to the next word in the array each time the animation completes an iteration.
.alternating {
animation-name: alternating-text;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: ease;
}
@keyframes alternating-text {
90% {
display: none;
}
}const texts = ['Java', 'Python', 'C', 'C++', 'C#', 'Javascript'];
const element = document.querySelector('.alternating');
let i = 0;
const listener = e => {
i = i < texts.length - 1 ? i + 1 : 0;
element.innerHTML = texts[i];
};
element.innerHTML = texts[0];
element.addEventListener('animationiteration', listener, false);https://codepen.io/chalarangelo/pen/MWNybVQ
[!CAUTION]
This implementation is not accessible to screen readers. If you want to make it accessible, consider using ARIA attributes or other techniques to ensure that the content is still readable by all users.
Snippets Css S Box Sizing Reset (`content/snippets/css/s/box-sizing-reset.md`)
title: Box-sizing reset language: css tags: [layout] cover: interior excerpt: Reset the box-model so that `width` and `height` are not affected by `border` or `padding`. listed: true dateModified: 2024-09-01
If you're not familiar with the box-model, it's a way to calculate the size of an element. The box-model consists of the content, padding, border, and margin properties.
By default, the width and height of an element are calculated based on the content property only. This means that if you add padding or border to an element, its width and height will increase.
But what if you want to include the padding and border in the width and height calculations? That's where box-sizing comes in. Using box-sizing: border-box, you can include the padding and border in the width and height calculations. You can also use box-sizing: inherit to pass down the box-sizing property from parent to child elements.
<div class="box">border-box</div>
<div class="box content-box">content-box</div>/* Reset the box-model */
div {
box-sizing: border-box;
}
/* Pass down the box-sizing property */
*,
*::before,
*::after {
box-sizing: inherit;
}
/* Example usage */
.box {
display: inline-block;
width: 120px;
height: 120px;
padding: 8px;
margin: 8px;
background: #F24333;
color: white;
border: 1px solid #BA1B1D;
border-radius: 4px;
}
/* Override the box-sizing property */
.content-box {
box-sizing: content-box;
}Snippets Css S Broken Image Fallback (`content/snippets/css/s/broken-image-fallback.md`)
title: Display a fallback for images that fail to load shortTitle: Broken image fallback language: css tags: [visual] cover: building-facade excerpt: Having trouble loading an image? Display a fallback message instead! listed: true dateModified: 2024-09-09
Ever wanted to display a message when an image fails to load? Perhaps you want to show the image's URL or a custom error message. Luckily, there's an easy way to do so.
The key to this technique lies in the way we handle the `` element. Instead of treating it as an image container, we'll treat it as a text container. This allows us to apply styles and pseudo-elements to it, which will only be displayed when the image fails to load.
Using the ::before and ::after pseudo-elements, we can display an error message and the image's URL. This way, users will know what went wrong and where the image was supposed to be.
The best part? This fallback message is only displayed when the image fails to load, so it won't interfere with the image itself.
<img src="/nowhere/to/be/found.jpg" />img {
display: block;
font-family: sans-serif;
font-weight: 300;
height: auto;
line-height: 2;
position: relative;
text-align: center;
width: 100%;
}
img::before {
content: "Sorry, this image is unavailable.";
display: block;
margin-bottom: 8px;
}
img::after {
content: "(url: " attr(src) ")";
display: block;
font-size: 12px;
}https://codepen.io/chalarangelo/pen/yLmOeVN
[!NOTE]
This fallback may require additional accessibility considerations. Make sure to test it with a screen reader to ensure it's accessible to all users. Also, always provide a meaningful
altattribute for your images to help users understand their content.
Snippets Css S Button Hover Transitions (`content/snippets/css/s/button-hover-transitions.md`)
title: CSS Button Transitions shortTitle: Button Transitions language: css tags: [animation] cover: painters-desk excerpt: Add some life to your buttons with these simple CSS transitions. listed: true dateModified: 2024-08-29
CSS transitions are a great way to add some life to your buttons. From growing and shrinking, to fill, swing and border animations, there are many ways to make your buttons more interactive.
https://codepen.io/chalarangelo/pen/qBedyER
Button grow animation
To create a grow animation on hover, you can use an appropriate transition to animate changes to the element. Use the :hover pseudo-class to change the transform to scale(1.1), growing the element when the user hovers over it.
[!TIP]
You can also use the new
scaleproperty to scale the element to a specific size. Simply replacetransform: scale(1.1)withscale: 110%.
.button-grow {
transition: all 0.3s ease-in-out;
}
.button-grow:hover {
transform: scale(1.1);
}Button shrink animation
Similarly, you can create a shrink animation on hover by using an appropriate transition to animate changes to the element. Use the :hover pseudo-class to change the transform to scale(0.8), shrinking the element when the user hovers over it.
[!TIP]
Again, you can use the new
scaleproperty to scale the element to a specific size. Simply replacetransform: scale(0.8)withscale: 80%.
.button-shrink {
transition: all 0.3s ease-in-out;
}
.button-shrink:hover {
transform: scale(0.8);
}Button fill animation
To create a fill animation on hover, set a color and background and use an appropriate transition to animate changes to the element. Use the :hover pseudo-class to change the background and color of the element when the user hovers over it.
.button-fill {
background: #fff;
color: #000;
transition: all 0.3s ease-in-out;
}
.button-fill:hover {
background: #000;
color: #fff;
}Button swing animation
For a swing effect, you can create an animation, using @keyframes and an appropriate transition to animate changes to the element. Use the :hover pseudo-class to apply an animation that uses transform to make the element swing.
.button-swing {
transition: all 0.2s ease-in-out;
}
.button-swing:hover {
animation: swing 1s ease;
animation-iteration-count: 1;
}
@keyframes swing {
15% {
transform: translateX(5px);
}
30% {
transform: translateX(-5px);
}
50% {
transform: translateX(3px);
}
65% {
transform: translateX(-3px);
}
80% {
transform: translateX(2px);
}
100% {
transform: translateX(0);
}
}Button border animation
Finally, for a border animation, you can create a border that changes color on hover. For this effect, you'll need to use the ::before and ::after pseudo-elements to create two boxes opposite each other above and below the box. Then, using the :hover pseudo-class, you can extend the width of those elements to 100% on hover and animate the change using transition.
.button-bordered {
border: none;
outline: none;
position: relative;
}
.button-bordered::before,
.button-bordered::after {
border: 0 solid transparent;
transition: all 0.3s;
content: '';
height: 0;
position: absolute;
width: 24px;
}
.button-bordered::before {
border-top: 2px solid #263059;
right: 0;
top: -4px;
}
.button-bordered::after {
border-bottom: 2px solid #263059;
bottom: -4px;
left: 0;
}
.button-bordered:hover::before,
.button-bordered:hover::after {
width: 100%;
}Snippets Css S Card Hover Effects (`content/snippets/css/s/card-hover-effects.md`)
title: Card hover effects language: css tags: [animation] cover: clouds-n-mountains excerpt: Create cards with hover effects, such as rotating, shifting and perspective transforms. listed: true dateModified: 2024-09-11
Cards, one of the most common layout elements in modern web design, provide a ton of opportunities for creative hover effects. Here are a few examples of card hover effects that you can use to make your website more interactive and engaging.
https://codepen.io/chalarangelo/pen/NWQNxQG
Rotating card
To create a two-sided card that rotates on hover, you first need a container element with two child elements, one for the front side and one for the back side of the card. You can then use the rotateY() function to rotate the card around the Y-axis, and the backface-visibility property to hide the back side of the card when it is not visible.
<div class="rotating-card">
<div class="card-side front"></div>
<div class="card-side back"></div>
</div>.rotating-card {
perspective: 150rem;
position: relative;
box-shadow: none;
background: none;
}
.rotating-card .card-side {
transition: all 0.8s ease;
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.rotating-card .card-side.back {
transform: rotateY(-180deg);
}
.rotating-card:hover .card-side.front {
transform: rotateY(180deg);
}
.rotating-card:hover .card-side.back {
transform: rotateY(0deg);
}Shifting card
For a shifting card, you will need to leverage CSS variables and a little bit of JavaScript to track the position of the mouse cursor and adjust the card's position accordingly. You'll use the mousemove event to track the cursor's position and calculate the relative distance between the cursor and the center of the card, using Element.getBoundingClientRect() to get the card's position and dimensions.
Then, using the CSS variables, you can apply a transform property to the card element that shifts it based on the cursor's position. To make the change smoother, use the transition property to animate the transformation.
.shifting-card {
transition: transform 0.2s ease-out;
transform: rotateX(calc(10deg * var(--dx, 0)))
rotateY(calc(10deg * var(--dy, 0)));
}const card = document.querySelector('.shifting-card');
const { x, y, width, height } = card.getBoundingClientRect();
const cx = x + width / 2;
const cy = y + height / 2;
const handleMove = e => {
const { pageX, pageY } = e;
const dx = (cx - pageX) / (width / 2);
const dy = (cy - pageY) / (height / 2);
e.target.style.setProperty('--dx', dx);
e.target.style.setProperty('--dy', dy);
};
card.addEventListener('mousemove', handleMove);Perspective card
Finally, for the perspective card, you will only need a transform with a perspective() function and a rotateY() function to create the perspective effect. The transition property will animate the transform attribute on hover.
.perspective-card {
transform: perspective(1500px) rotateY(15deg);
transition: transform 1s ease 0s;
}
.perspective-card:hover {
transform: perspective(3000px) rotateY(5deg);
}Snippets Css S Card Image Cutout (`content/snippets/css/s/card-image-cutout.md`)
title: Card with image cutout language: css tags: [visual] cover: radio-monstera excerpt: Cutout effects seem tricky to implement, but they really aren't. CSS has you covered! listed: true dateModified: 2024-09-13
Cutout effects are a great way to add visual interest to your website. They can be used to create unique layouts, highlight important content, or simply make your website more visually appealing. One common way to create a cutout effect is to use an image cutout, where an image is displayed inside a container with a cutout shape.
To create this effect, you'll need a container element with a colored background and a card element containing the image you want to display. You can then use the ::before pseudo-element to add a border around the image, matching the container element's background and creating the illusion of a cutout in the card.
https://codepen.io/chalarangelo/pen/ZEgWBRo
<div class="container">
<div class="card">
<figure>
<img src="/path/to/img.jpg"/>
</figure>
<p class="content">Card content</p>
</div>
</div>.container {
background: #f3f1fe;
}
.card {
background: #fff;
box-shadow: 0 0 5px -2px rgba(0, 0, 0, 0.1);
}
.card figure {
width: 120px;
height: 120px;
border-radius: 50%;
margin-top: -60px;
position: relative;
}
.card figure::before {
content: "";
border-radius: inherit;
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
transform: translate(-50%, -50%);
border: 1rem solid #f3f1fe;
box-shadow: 0 1px rgba(0, 0, 0, 0.1);
}
.card figure img {
border-radius: inherit;
width: 100%;
height: 100%;
object-fit: cover;
}Snippets Css S Centering (`content/snippets/css/s/centering.md`)
title: Centering content with CSS language: css tags: [layout] cover: malibu excerpt: Centering content with CSS might often feel difficult. Here are 4 easy ways you can do it. listed: true dateModified: 2024-05-07
How to center a div has become a little bit of a joke in the web development community, and for good reason. Not only is it a common task, but it can also be a bit tricky to get right, especially when you're new to CSS. Luckily, modern CSS solutions exist for pretty much any scenario you might encounter.
Flexbox centering
Using flexbox to vertically and horizontally center content is usually the preferred method. All it takes is three lines of code in the container element to set display: flex and then center the child element vertically and horizontally using align-items: center and justify-content: center respectively.
<div class="flexbox-centering">
<div class="content">Content</div>
</div>.flexbox-centering {
display: flex;
justify-content: center;
align-items: center;
}https://codepen.io/chalarangelo/pen/wvbwQKg
Grid centering
Using the grid module is very similar to flexbox and also a common technique, especially if you are already using grid in your layout. The only difference from the previous technique is that you use display: grid in the container element and then center the child element the same way as before.
<div class="grid-centering">
<div class="content">Content</div>
</div>.grid-centering {
display: grid;
justify-content: center;
align-items: center;
}https://codepen.io/chalarangelo/pen/OJYLaNb
Transform centering
Transform centering uses, as the name implies, CSS transforms to center an element. It depends on the container element having a position: relative, allowing the child element to utilize position: absolute to position itself. Then left: 50% and top: 50% are used to offset the child element and transform: translate(-50%, -50%) to negate its position.
<div class="transform-centering">
<div class="content">Content</div>
</div>.transform-centering {
position: relative;
}
.transform-centering > .content {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}https://codepen.io/chalarangelo/pen/OJYLapp
Table centering
Last but not least, table centering is an older technique which you might favor when working with older browsers. It depends on the use of display: table in the container element, making it behave like a element. This allows the child element to use `display: table-cell`, behaving like a, in combination with text-align: center and vertical-align: middle to center itself horizontally and vertically. Note that the parent element must have a fixed width and height.
<div class="table-centering">
<div class="content">Content</div>
</div>.table-centering {
display: table;
height: 100%;
width: 100%;
}
.table-centering > .content {
display: table-cell;
text-align: center;
vertical-align: middle;
}https://codepen.io/chalarangelo/pen/jOoNQmZ
Snippets Css S Circular Progress Bar (`content/snippets/css/s/circular-progress-bar.md`)
title: How can I create a circular progress bar using only CSS? shortTitle: Circular progress bar language: css tags: [visual,animation] cover: clouds-over-mountains excerpt: Circular progress bars are fairly common in the web. Luckily, some new CSS features make it easier than ever to create them. listed: true dateModified: 2023-12-23
Circular progress bars are a fairly common element in today's websites. Yet, to many developers, they seem like quite the intimidating challenge. The truth of the matter is that getting the basics right is not that hard. In fact, with the help of some new CSS features, it's easier than ever.
Anatomy of a circular progress bar
A circular progress bar is, at the simplest, two circles stacked on top of each other. The bottom circle is the background, and the top circle is the progress indicator. We'll get to how we fill the progress indicator in a bit, but the basic structure can be easily built using an <svg> element and a little bit of CSS.
<svg width="250" height="250" viewBox="0 0 250 250">
<circle class="bg"
cx="125" cy="125" r="115" fill="none" stroke="#ddd" stroke-width="20"
></circle>
<circle class="fg"
cx="125" cy="125" r="115" fill="none" stroke="#5394fd" stroke-width="20"
></circle>
</svg>circle.fg {
transform: rotate(-90deg);
transform-origin: 125px 125px;
}As you can see, the only piece of CSS we need to get the basic structure right is a transform property. We rotate the foreground circle by 90 degrees, and we set the transform-origin to the center of the circle. This way, the circle is rotated around its center, and the progress indicator starts at the top.
Doing the math
Before we move forward, we might as well take a moment to understand the math behind the code.
The two values we need to decide are the size of the progress bar and the width of the stroke. For this example, we settled on a size of 250px and a stroke width of 20px. We'll use these values to calculate the rest of the values we need.
The size is use to set the width and height attributes of the SVG element, as well as the viewBox attribute. Dividing it by two, we get 125px, which corresponds to the coordinates of the center of the image. This value is used to set the cx and cy attributes of the circles.
Taking into account the stroke width, we can calculate the radius of the circle. The radius is the distance from the center of the circle to the edge. In this case, the radius is 115px, which is the size of the image, minus the stroke width, divided by two.
Finally, we can calculate the circumference of the circle. The circumference is the length of the edge of the circle. In this case, the circumference is 722.5px, which is 2 * π * 115px.
| Variable | Value | Formula |
|---|---|---|
| size | 250px |
N/A (user defined) |
| stroke | 20px |
N/A (user defined) |
| center | 125px |
size / 2 |
| radius | 115px |
(size - stroke) / 2 |
| circumference | 722.5px |
2 * π * radius |
These numbers will start coming in handy as we move forward, but I promise this is almost all the math we'll need to do.
Filling the progress indicator
Now that we have the basic structure in place, we need to fill the progress indicator. To do this, we'll use the stroke-dasharray property, which takes alternating values for lengths and dashes.
To create a progress bar, we want to pass two values: the length of the filled part, and the length of the empty part. To get the filled part we multiply the progress percentage by the circumference of the circle. To get the empty part, we subtract the filled part from the circumference.
Supposing we want to fill 50% of the circle, the SVG code would look like this:
<svg width="250" height="250" viewBox="0 0 250 250">
<circle class="bg"
cx="125" cy="125" r="115" fill="none" stroke="#ddd" stroke-width="20"
></circle>
<circle class="fg"
cx="125" cy="125" r="115" fill="none" stroke="#5394fd" stroke-width="20"
stroke-dasharray="361.25 361.25"
></circle>Making the progress bar dynamic
Hardcoding the stroke-dasharray value is not very useful. We want to be able to set the progress percentage dynamically. This is where CSS variables and the math from before come into play.
Given a --progress variable, we can calculate the stroke-dasharray relatively easily. Knowing we will need most of the values from before, we can set them as CSS variables, too. Even better, most of the SVG attributes we want to set can be manipulated using CSS.
Here's what the restructured code looks like:
<svg
width="250" height="250" viewBox="0 0 250 250"
class="circular-progress" style="--progress: 50"
>
<circle class="bg"></circle>
<circle class="fg"></circle>
</svg>.circular-progress {
--size: 250px;
--half-size: calc(var(--size) / 2);
--stroke-width: 20px;
--radius: calc((var(--size) - var(--stroke-width)) / 2);
--circumference: calc(var(--radius) * pi * 2);
--dash: calc((var(--progress) * var(--circumference)) / 100);
}
.circular-progress circle {
cx: var(--half-size);
cy: var(--half-size);
r: var(--radius);
stroke-width: var(--stroke-width);
fill: none;
stroke-linecap: round;
}
.circular-progress circle.bg {
stroke: #ddd;
}
.circular-progress circle.fg {
transform: rotate(-90deg);
transform-origin: var(--half-size) var(--half-size);
stroke-dasharray: var(--dash) calc(var(--circumference) - var(--dash));
transition: stroke-dasharray 0.3s linear 0s;
stroke: #5394fd;
}This may look like a lot, but it's mostly just setting CSS variables and using them to calculate the values we need. One cool thing I want to point out is that the pi constant is available as part of the calc() function! I didn't know this until I started writing this article, and I'm very excited about it.
At this point, if you use some JavaScript to manipulate the value of the --progress variable, you'll see the progress bar fill up. The added transition property will make the progress bar animate smoothly.
Timed progress bar
Have you ever watched an ad inside a mobile game? You know, the ones that give you a reward if you watch the whole thing? They usually have a progress bar that fills up as the ad plays. Or it empties as you watch, much like a countdown timer. Whatever flavor you might have seen, they are variations of the same concept.
How can we create a progress bar that fills up over a predetermined amount of time? We could go about it using JavaScript and Window.requestAnimationFrame(), but that wouldn't be very cool. Instead, we can use the animation property to animate the --progress variable from 0 to 100 over a set amount of time.
@keyframes progress-animation {
from {
--progress: 0;
}
to {
--progress: 100;
}
}If you try hooking this up to our SVG, you'll notice it doesn't work exactly like you'd think. This is due to the fact that the browser doesn't really know what to do with the --progress variable. It doesn't know it's a number, so it doesn't know how to animate it.
Luckily, CSS has come up with a solution for this. The @property rule allows us to define custom properties and tell the browser what type they are. In this case, we want to tell the browser that --progress is a number.
@property --progress {
syntax: "<number>";
inherits: false;
initial-value: 0;
}Now that the browser knows what to do with the --progress variable, we can hook it up to the animation.
.circular-progress {
animation: progress-animation 5s linear 0s 1 forwards;
}This will animate the --progress variable from 0 to 100 over 5 seconds. The forwards keyword tells the browser to keep the final value of the animation. Without it, the progress bar would reset to 0 after the animation is done. You can create the opposite effect by setting the animation-direction property to reverse and using backwards instead of forwards.
Putting it all together
We've covered a lot of ground in this article. We've gone from a simple SVG element to a fully functional progress bar. We've used CSS variables, math functions, and even a new CSS feature. Let's take a look at the final code.
<svg width="250" height="250" viewBox="0 0 250 250" class="circular-progress">
<circle class="bg"></circle>
<circle class="fg"></circle>
</svg>.circular-progress {
--size: 250px;
--half-size: calc(var(--size) / 2);
--stroke-width: 20px;
--radius: calc((var(--size) - var(--stroke-width)) / 2);
--circumference: calc(var(--radius) * pi * 2);
--dash: calc((var(--progress) * var(--circumference)) / 100);
animation: progress-animation 5s linear 0s 1 forwards;
}
.circular-progress circle {
cx: var(--half-size);
cy: var(--half-size);
r: var(--radius);
stroke-width: var(--stroke-width);
fill: none;
stroke-linecap: round;
}
.circular-progress circle.bg {
stroke: #ddd;
}
.circular-progress circle.fg {
transform: rotate(-90deg);
transform-origin: var(--half-size) var(--half-size);
stroke-dasharray: var(--dash) calc(var(--circumference) - var(--dash));
transition: stroke-dasharray 0.3s linear 0s;
stroke: #5394fd;
}
@property --progress {
syntax: "<number>";
inherits: false;
initial-value: 0;
}
@keyframes progress-animation {
from {
--progress: 0;
}
to {
--progress: 100;
}
}And here's a CodePen of the code in action:
https://codepen.io/chalarangelo/pen/mdodgeL
Conclusion
Using modern HTML and CSS, we created a circular progress bar. This setup serves as a good starting point for you to experiment with. You can use it as is, or you can extend it to suit your needs, sprinkling in a little bit of JavaScript if you need to. You can even convert it into a Web Component or a React component for your projects.
Snippets Css S Clearfix (`content/snippets/css/s/clearfix.md`)
title: How to clear floats with a clearfix shortTitle: Clearfix language: css tags: [layout] cover: memories-of-pineapple-3 excerpt: Eliminate the need for non-semantic markup to clear floats. listed: true dateModified: 2024-08-06
When using the old-school float property to build layouts, you may encounter issues with elements not clearing their floated children. This can lead to unexpected layout problems, such as elements overlapping or not aligning properly.
To fix this issue, the clearfix hack was developed, which allows an element to self-clear its children without the need for additional non-semantic markup.
What you need to do is to create a pseudo-element with content: '' and apply clear: both to it. This will make the element clear past both left and right floats, ensuring that the layout behaves as expected.
For this technique to work properly, make sure there are no non-floating children in the container and that there are no tall floats before the container with the clearfix but in the same formatting context (e.g. floated columns).
[!NOTE]
This is only useful if you are using
floatto build layouts. Consider using a more modern approach, such as the flexbox or grid layout.
<div class="clearfix">
<div class="floated">float a</div>
<div class="floated">float b</div>
<div class="floated">float c</div>
</div>.clearfix::after {
content: '';
display: block;
clear: both;
}
.floated {
float: left;
padding: 4px;
}Snippets Css S Code Reviews (`content/snippets/css/s/code-reviews.md`)
title: How do you review CSS code in Pull Requests? shortTitle: CSS code reviews language: css tags: [webdev] cover: green-css excerpt: Reviewing CSS code is a skill that takes time to master. Here are some tips from my personal experience to help you get started. listed: true dateModified: 2023-05-21
Reviewing CSS code is a bit different than reviewing JavaScript code. In fact, many developers pay little attention to CSS, which can lead to problems down the line. Yet, many developers don't know how to review CSS code properly. In this post, we'll go over some of the things you should look for when reviewing CSS code in Pull Requests.
Visual check
The very first step when reviewing CSS is to check if the result looks right. This might mean comparing the end result to a design mockup or checking if every interaction behaves according to the animation principles the team has agreed upon. Responsiveness is also a key factor to consider, so you should check if the page looks right on different screen sizes. The visual check is probably the most straightforward and important step, but unfortunately many developers stop there.
Code style
Your team should have set up a linter and formatter for CSS and, if you haven't, you should do it as soon as possible. This will help you enforce a consistent code style and make the code easier to read and maintain. Provided that's the case, you should check for conventions that the linter can't automatically enforce. These often include naming conventions, proper documentation or the use of CSS custom properties in place of hard-coded values.
Specificity
CSS selectors can be easily abused, causing headaches later down the line. Having clear conventions usually resolves a lot of issues, but things can slip through the cracks. Ensuring specificity is as low as possible and that selectors are not too generic or overly complex will help increase the code's maintainability.
Leftovers
In an ideal scenario, the Pull Request author has a clear vision of the CSS they are writing and everything works out perfectly the first time. As you know, that's rarely the case, meaning experimentation and changes will happen during development. As the code changes, some old code might hang around without contributing anything to the page. One of the most common examples I've stumbled upon are flexbox-related properties for non-flexbox elements. These take a bit of time to spot, but they can be easily removed, saving you problems in the future.
Performance
CSS performance is very often overlooked. Simple rules, deduplication and minimum overrides are some of the things that can be done to improve performance. Understandably, this sort of opportunity is hard to spot, but it's worth keeping an eye out for. That being said, don't go overboard with performance optimizations. If you're not sure if something is worth it, you should probably leave it as is.
Snippets Css S Content Aware Height Transition (`content/snippets/css/s/content-aware-height-transition.md`)
title: Content-aware height transition language: css tags: [animation] cover: washed-ashore excerpt: Dynamically transition an element's height, based on its content. listed: true dateModified: 2024-08-24
As you may be aware, the height property cannot be transitioned from 0 to auto directly. Instead, we usually rely on max-height to transition an element's height from 0 to a known value. However, when the height of the element is unknown, we can use a combination of JavaScript and CSS variables to transition the height from 0 to auto.
The trick here is to leverage CSS custom properties to alter the max-height. Then, using JavaScript, we can change the value of the custom property to the actual height of the element's content.
So, how do we do that, exactly? Provided we've defined a --max-height CSS variable with, we can use Element.scrollHeight and CSSStyleDeclaration.setProperty() to set the value of the custom property to the current height of the element. This way, we can essentially transition the height of the element from 0 to auto.
In order to complete the effect and apply it on hover, we'll use the :hover pseudo-class to change the max-height to the value of the --max-height variable set by JavaScript. We'll also add a transition for the max-height property to animate the height change.
And voilà! You've got yourself a content-aware height transition.
https://codepen.io/chalarangelo/pen/OJeKKBZ
[!CAUTION]
This trick causes a reflow on each animation frame, which will be laggy if there are a large number of elements beneath the element that is transitioning in height.
<div class="trigger">
Hover me to see a height transition.
<div class="el">Additional content</div>
</div>.el {
transition: max-height 0.3s;
overflow: hidden;
max-height: 0;
}
.trigger:hover > .el {
max-height: var(--max-height);
}const el = document.querySelector('.el');
const height = el.scrollHeight;
el.style.setProperty('--max-height', height + 'px');Snippets Css S Current Color (`content/snippets/css/s/current-color.md`)
title: The currentColor CSS keyword language: css tags: [visual] cover: picking-berries excerpt: The `currentColor` CSS keyword is a nifty alternative to custom properties for simple use cases. listed: true dateModified: 2022-11-30
Modern CSS supports custom properties, yet the currentColor keyword precedes them by a few years. Thus, you might still find it in the wild and it is worth knowing what it does and how it works.
<p>My <span>background</span> is the same color as my <a href="#">text</a>.</p>p {
color: #101010;
}
p, p > * {
background-color: currentColor;
}
a {
color: #0077ff;
}
span {
color: #fd203a;
}currentColor contains the current value of the color property of the element. It is useful when you want to use the same color for multiple properties, such as border-color or background-color. It also respects the cascade, so if no value is provided for color, it will use the value of the color property of the parent element.
Snippets Css S Custom Checkbox Radio (`content/snippets/css/s/custom-checkbox-radio.md`)
title: Styling checkboxes and radio buttons language: css tags: [visual,animation] cover: interior-8 excerpt: Learn how to create customized and animated checkboxes and radio buttons with CSS. listed: true dateModified: 2024-09-14
One of the most common pain points in web development is styling checkboxes and radio buttons. They are notoriously difficult to style consistently across browsers and platforms. However, with a little CSS magic, you can create custom checkboxes and radio buttons that look great and are easy to use.
https://codepen.io/chalarangelo/pen/QWeNvea
Custom checkboxes
For a custom checkbox, the best solution I've found is to use an <svg> element to create the checkmark symbol and insert it via the `` element to create a reusable SVG icon.
You can then create a container and use flexbox to create the appropriate layout for the checkboxes. Hide the `` element and use the <label> associated with it to display a checkbox and the provided text.
Then, using stroke-dashoffset, you can animate the check symbol on state change. Finally, use transform: scale(0.9) via a CSS animation to create a zoom animation effect.
<svg class="checkbox-symbol">
<symbol id="check" viewbox="0 0 12 10">
<polyline
points="1.5 6 4.5 9 10.5 1"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
></polyline>
</symbol>
</svg>
<div class="checkbox-container">
<input class="checkbox-input" id="apples" type="checkbox" />
<label class="checkbox" for="apples">
<span>
<svg width="12px" height="10px">
<use xlink:href="#check"></use>
</svg>
</span>
<span>Apples</span>
</label>
<input class="checkbox-input" id="oranges" type="checkbox" />
<label class="checkbox" for="oranges">
<span>
<svg width="12px" height="10px">
<use xlink:href="#check"></use>
</svg>
</span>
<span>Oranges</span>
</label>
</div>/* Detailed source-code truncated for AI context efficiency. */Custom radio buttons
For a custom radio button, you can use pseudo-elements to do the heavy lifting. Same as before, create a container and use flexbox to create the appropriate layout for the radio buttons. Reset the styles on the `` element and use it to create the outline and background of the radio button.
Then, use the ::before pseudo-element to create the inner circle of the radio button. Use transform: scale(1) and a CSS transition to create an animation effect on state change.
<div class="radio-container">
<input class="radio-input" id="apples" type="radio" name="fruit" />
<label class="radio" for="apples">Apples</label>
<input class="radio-input" id="oranges" type="radio" name="fruit" />
<label class="radio" for="oranges">Oranges</label>
</div>.radio-container {
box-sizing: border-box;
background: #ffffff;
color: #222;
height: 64px;
display: flex;
justify-content: center;
align-items: center;
flex-flow: row wrap;
}
.radio-container * {
box-sizing: border-box;
}
.radio-input {
appearance: none;
background-color: #ffffff;
width: 16px;
height: 16px;
border: 1px solid #cccfdb;
margin: 0;
border-radius: 50%;
display: grid;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.radio-input::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
transform: scale(0);
transition: 0.3s transform ease-in-out;
box-shadow: inset 6px 6px #ffffff;
}
.radio-input:checked {
background: #0077ff;
border-color: #0077ff;
}
.radio-input:checked::before {
transform: scale(1);
}
.radio {
cursor: pointer;
padding: 6px 8px;
}
.radio:not(:last-child) {
margin-right: 6px;
}Snippets Css S Custom Scrollbar (`content/snippets/css/s/custom-scrollbar.md`)
title: Create a custom scrollbar with CSS language: css tags: [visual] cover: sea-view excerpt: Customize the scrollbar style for elements with scrollable overflow, using just CSS. listed: true dateModified: 2024-09-17
Scrollbar styling is one of the longest-awaited features in CSS. While it's not officially supported, there are still ways to style scrollbars in certain browsers.
[!CAUTION]
Scrollbar styling doesn't appear to be on any standards track. This technique only works on WebKit-based browsers (Chrome, Edge, Safari).
In general, you can use the ::-webkit-scrollbar pseudo-element to style the scrollbar element, ::-webkit-scrollbar-track to style the scrollbar track (the background of the scrollbar), and ::-webkit-scrollbar-thumb to style the scrollbar thumb (the draggable element).
Putting everything together, you can create a style for the scrollbar like this:
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #4a7856;
border-radius: 12px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #70bceb;
border-radius: 12px;
}https://codepen.io/chalarangelo/pen/JjgXePq
Snippets Css S Details Tabbed Content (`content/snippets/css/s/details-tabbed-content.md`)
title: Creating tabbed content without JavaScript shortTitle: Details tabbed content language: css tags: [interactivity] cover: flower-portrait-6 excerpt: Leverage modern HTML and CSS capabilities to create a tabbed content component without JavaScript. listed: true dateModified: 2025-05-26
A few weeks back, I was working on a tabbed content component and wondered if I could leverage the `` HTML element to create a more semantic and accessible solution. After some experimentation, I found that it was indeed possible, but there's a small caveat: it needs the ::details-content pseudo-element to work properly.
As you may be aware by now, the element creates collapsable sections of content. Adding the same `name` attribute to multiple elements allows you to create an accordion-style component. We can build on top of that to create a tabbed content component.
<div class="tabs-container">
<details name="tabs" open>
<summary>First tab</summary>
<div><p>First tab content</p></div>
</details>
<details name="tabs">
<summary>Second tab</summary>
<div><p>Second tab content</p></div>
</details>
</div>In order for this trick to work, we'll have to wrap all of our elements in a `` or similar container and make it a grid. We'll then set up our grid to be N x 2, where N is the number of tabs we want.
.tabs-container {
position: relative;
display: grid;
grid-template-rows: auto 1fr;
/* For 2 tabs, adjust to your needs */
grid-template-columns: repeat(2, auto);
}The first row will contain our tab handles (the `` elements), and the second row will contain the content of each tab. We'll use the appropriate grid-row and grid-column properties to set these up correctly.
.tabs-container > details > summary {
grid-row: 1;
}
.tabs-container > details > div {
grid-column: 1 / -1;
}In order for the open tab to display correctly, we'll need to set the `` element, along with the ::details-content pseudo-element to display: contents. This essentially means that the element will not generate a box, but its children will be displayed as if they were direct children of the parent element. This allows us to keep the semantic structure of the HTML while still achieving the desired layout.
.tabs-container > details {
display: contents;
}
.tabs-container > details[open]::details-content {
display: contents;
}And that's pretty much all that's needed. Slap a few styles to make it pretty and you've got yourself a JavaScript-free tabbed content component.
https://codepen.io/chalarangelo/pen/JoodqXo
<div class="tabs-container">
<details name="tabs" open>
<summary>First tab</summary>
<div><p>First tab content</p></div>
</details>
<details name="tabs">
<summary>Second tab</summary>
<div><p>Second tab content</p></div>
</details>
</div>.tabs-container {
position: relative;
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: repeat(2, auto);
}
.tabs-container > details {
display: contents;
}
.tabs-container > details > summary {
display: flex;
justify-content: center;
grid-row: 1;
}
/* Hides the ::marker pseudo-element */
.tabs-container > details > summary::marker {
display: none;
content: "";
}
.tabs-container > details > div {
grid-column: 1 / -1;
}
.tabs-container > details[open]::details-content {
display: contents;
}Snippets Css S Drop Caps (`content/snippets/css/s/drop-caps.md`)
title: Drop caps with pure CSS shortTitle: Drop caps language: css tags: [visual] cover: bamboo-lamp excerpt: Style the first letter of the first paragraph in your text, using a simple CSS selector, listed: true dateModified: 2024-08-23
The ::first-letter CSS pseudo-element is used to style the first letter of the first line of a block-level element. This is often used to create a drop cap effect, where the first letter is larger than the rest of the text.
Most drop cap effects are only applied to the first paragraph, so you'll need to combine the previous selector with the :first-child pseudo-class to achieve this effect.
https://codepen.io/chalarangelo/pen/oNrKNWK
Then, you can style the first letter of the first paragraph using the ::first-letter pseudo-element. More often than not, you'll want to alter its font-size, font-weight, line-height, and color. Additionally, to make it look more like a drop cap, you can float it to the left and add some margin.
p:first-child::first-letter {
color: #5f79ff;
float: left;
margin: 0 8px 0 4px;
font-size: 3rem;
font-weight: bold;
line-height: 1;
}Snippets Css S Dynamic Shadow (`content/snippets/css/s/dynamic-shadow.md`)
title: Dynamic shadow language: css tags: [visual] cover: couch-laptop excerpt: Learn how to create a shadow similar to `box-shadow`, but based on the colors of the element itself. listed: true dateModified: 2024-09-04
Ever wanted to create a shadow that matches the colors of the element itself? Unfortunately, box-shadow is quite limited in that regard. However, you can create a similar effect using a pseudo-element and some clever CSS.
Given an element with position: relative, you can set position: absolute on its ::after pseudo-element to fill the available space. By setting background: inherit, the pseudo-element will inherit the background of the parent element.
In order to create the shadow, you can then slightly offset the pseudo-element using top and apply a blur() filter to create a shadow, and make it semi-transparent using opacity.
Finally, you can position the pseudo-element behind its parent by setting z-index: -1 on the pseudo-element. Due to the way the stacking context works, the parent itself should not define a value for z-index but rather its parent needs to have a z-index value for the element itself to display above the ::after pseudo-element.
.container {
z-index: 1;
}
.dynamic-shadow {
position: relative;
background: linear-gradient(75deg, #6d78ff, #00ffb8);
}
.dynamic-shadow::after {
content: '';
width: 100%;
height: 100%;
position: absolute;
background: inherit;
top: 0.5rem;
filter: blur(0.4rem);
opacity: 0.7;
z-index: -1;
}This effect can work well with images, gradients or any other sort of background that you want to create a shadow for. You can also adjust the blur() value to create a more or less pronounced shadow effect.
https://codepen.io/chalarangelo/pen/XWvmNEQ
Snippets Css S Easing Variables (`content/snippets/css/s/easing-variables.md`)
title: CSS easing variables language: css tags: [animation] cover: curve excerpt: Learn how to use the `cubic-bezier()` class of easing functions and create beautiful animations that stand out. listed: true dateModified: 2021-06-12
Most web developers use the built-in ease, ease-in, ease-out or ease-in-out functions for most use-cases of transition-timing-function in their designs. While these are perfectly fine for everyday use, there's a far more powerful, yet intimidating option available, the bezier-curve() function.
Using the bezier-curve() we can easily define custom easing variables that can help our designs pop out. In fact the built-in functions mentioned above can also be written using the bezier-curve() function. Here's a handful of useful easing functions stored in CSS variables for ease of use:
:root {
/* ease-in corresponds to cubic-bezier(0.42, 0, 1.0, 1.0) */
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
--ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
--ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
--ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
/* ease-out corresponds to cubic-bezier(0, 0, 0.58, 1.0) */
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
/* ease-in-out corresponds to cubic-bezier(0.42, 0, 0.58, 1.0) */
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}Snippets Css S Engraved Embossed Text (`content/snippets/css/s/engraved-embossed-text.md`)
title: Engraved and embossed text language: css tags: [visual] cover: memories-of-pineapple-2 excerpt: Make the text appear engraved or embossed into the background, using CSS. listed: true dateModified: 2024-09-16
Engraved and embossed text effects are pretty similar to each other. The difference is that the engraved text appears to be carved into the background, while the embossed text appears to be raised from the background.
For both effects, you'll need to use background-color combined with background-clip to clip the background to the text. Then, you can use color to set the text color and text-shadow to add a shadow to the text.
For the engraved text, the text-shadow should be lighter than the background, while for the embossed text, it should be darker. Additionally, the placement of the shadow should be slightly different for each effect.
.engraved-text {
background-color: #666666;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
text-shadow: 3px 5px 1px rgba(245, 245, 245, 0.5);
}
.embossed-text {
background-color: #666666;
background-clip: text;
-webkit-background-clip: text;
color: #f0f0f0;
text-shadow: 1px 4px 4px #555555;
}https://codepen.io/chalarangelo/pen/dyxMzZd
Snippets Css S Evenly Distributed Children (`content/snippets/css/s/evenly-distributed-children.md`)
title: Evenly distributed children language: css tags: [layout] cover: little-bird excerpt: Evenly distribute child elements within a parent element, using Flexbox. listed: true dateModified: 2024-09-06
Have you ever struggled with distributing child elements evenly within a parent element? Flexbox makes it easy to achieve this effect, using the justify-content property.
All you really need is a container with display: flex. Then, you can use justify-content: space-between to evenly distribute the child elements on the main axis.
If your container is horizontal (flex-direction: row), the first item will be positioned at the left edge, while the last item will be positioned at the right edge. If your container orientation is vertical (flex-direction: column), the first item will be positioned at the top edge, while the last item will be positioned at the bottom edge.
Alternatively, you can use justify-content: space-around to distribute the children with space around them, instead of between them. In this case the space between each pair of adjacent items will be half the space before the first item and after the last item.
.evenly-distributed-children {
display: flex;
justify-content: space-between;
}
.evenly-spaced-children {
display: flex;
justify-content: space-around;
}[!NOTE]
You may want to check the Flexbox cheatsheet for more information on Flexbox properties and values.
Snippets Css S Fit Image In Container (`content/snippets/css/s/fit-image-in-container.md`)
title: How can I fit an image in a container with CSS? shortTitle: Fit image in container language: css tags: [layout,visual] cover: succulent-3 excerpt: Fit and position an image appropriately inside its container while preserving its aspect ratio with this handy technique. listed: true dateModified: 2024-08-26
Have you ever wanted to fit an image inside a container while preserving its aspect ratio? This is a common requirement when working with images, and it can be achieved using the object-fit and object-position properties in CSS.
Generally, you can use object-fit: contain to fit the entire image within the container while preserving its aspect ratio. On the other hand, object-fit: cover will fill the container with the image while preserving its aspect ratio.
You can also use object-position to position the image within the container. For example, object-position: center will center the image, while object-position: right top will position it at the top right corner.
.image-contain {
object-fit: contain;
object-position: center;
}
.image-cover {
object-fit: cover;
object-position: right top;
}https://codepen.io/chalarangelo/pen/KKOwRRZ
Snippets Css S Flexbox Cheatsheet (`content/snippets/css/s/flexbox-cheatsheet.md`)
title: Flexbox Cheat Sheet language: css tags: [layout,flexbox,cheatsheet] 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. listed: true dateModified: 2021-06-12
Container
display: flexordisplay: inline-flex: creates a flex context (or an inline flex context) for direct children of this elementflex-directiondetermines 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 bottomcolumn-reverse: vertical, bottom to top
flex-wrapdetermines if flex items will try to fit in one line, valid values are:nowrap(default): all flex items will be on one linewrap: flex items will wrap onto multiple lines, top to bottomwrap-reverse: flex items will wrap onto multiple lines, bottom to top
flex-flow: shorthand combiningflex-directionandflex-wrap- Formal syntax:
flex-flow: <'flex-direction'> || <'flex-wrap'>
- Formal syntax:
justify-contentdefines the alignment along the main axis, valid values are:flex-start(default): pack flex items from the startflex-end: pack flex items from the endstart: pack items from the startend: pack items from the endleft: pack items from the leftright: pack items from the rightcenter: pack items around the centerspace-around: distribute items evenly with equal space around themspace-between: distribute items evenly with equal space between themspace-evenly: distribute items evenly, ensuring equal space between any two itemsstretch: distribute items evenly, stretching auto-sized items to fit the container
align-itemsdefines the alignment along the cross axis, valid values are:flex-start(default): pack flex items from the startflex-end: pack flex items from the endstart: pack items from the startend: pack items from the endcenter: pack items around the centerbaseline: align items based on their baselinesstretch: stretch items to fill the container
align-contentdefines the alignment of extra space along the cross axis, valid values are:flex-start(default): pack flex items from the startflex-end: pack flex items from the endstart: pack items from the startend: pack items from the endcenter: pack items around the centerspace-around: distribute items evenly with equal space around themspace-between: distribute items evenly with equal space between themspace-evenly: distribute items evenly, ensuring equal space between any two itemsstretch: distribute items evenly, stretching auto-sized items to fit the container
Items
flex-growdetermines 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
- Accepts a single positive number (unitless), default value is
flex-shrinkdetermines 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
- Accepts a single positive number (unitless), default value is
flex-basisdetermines the initial size of a flex item before the remaining space is distributed- Can use any valid
widthvalue, intrinsic size values,auto(default) orcontent automeans "look at mywidthorheightproperty", whereascontentis used for automatic sizing
- Can use any valid
flex: shorthand combiningflex-grow,flex-shrinkandflex-basis- Formal syntax:
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
- Formal syntax:
align-selfallows the item to override the defaultalign-itemsspecified by the container- Valid values are the same as those of the
align-itemsproperty in the container
- Valid values are the same as those of the
orderdetermines the ordering of the item- Accepts an integer value
- Items in a container are sorted by ascending
ordervalue and then by their source code order - Might cause accessibility issues if used incorrectly
Snippets Css S Floating Or Sticky List Titles (`content/snippets/css/s/floating-or-sticky-list-titles.md`)
title: List with floating or sticky section headings language: css tags: [visual] cover: pop-of-green excerpt: Learn how to create a list with floating or sticky headings for each section. listed: true dateModified: 2024-08-29
Ever wanted to create a list with floating or sticky headings for each section? You absolutely can, using some clever CSS tricks.
https://codepen.io/chalarangelo/pen/ExqjMaP
[!NOTE]
This article uses the semantic HTML
<dl>,<dt>, and<dd>elements. You can easily replace them with your own HTML structure, as needed.
Floating headings
In order to create a list with floating headings for each section, you can use overflow-y: auto to allow the list container to overflow vertically. Then, use display: grid on the inner container (<dl>) to create a layout with two columns.
Set headings (<dt>) to grid-column: 1 and content (<dd>) to grid-column: 2. Finally, apply position: sticky and top: 0.5rem to headings to create a floating effect.
.floating-stack {
overflow-y: auto;
}
.floating-stack > dl {
display: grid;
grid-template-columns: 2.5rem 1fr;
align-items: center;
}
.floating-stack dt {
position: sticky;
top: 0.5rem;
grid-column: 1;
}
.floating-stack dd {
grid-column: 2;
}Sticky headings
Similarly, for the sticky headings, you'll need to use overflow-y: auto to allow the list container (<dl>) to overflow vertically. Then, simply set headings (<dt>) to position: sticky and top: 0 to create a sticky effect. You can also style the headings to make them stand out.
.sticky-stack {
overflow-y: auto;
}
.sticky-stack dt {
position: sticky;
top: 0;
}Snippets Css S Fluid Typography (`content/snippets/css/s/fluid-typography.md`)
title: Fluid typography with CSS clamp shortTitle: Fluid typography language: css tags: [visual] cover: shell-focus excerpt: Learn how to create text that scales according to the viewport width. listed: true dateModified: 2024-08-27
Fluid typography is a technique that allows text to scale according to the viewport width. This is particularly useful for responsive designs, where you want the text to grow or shrink based on the available space.
[!NOTE]
While fluid typography is a cool technique, hand-crafted typographic scales can often look more polished. You may want to read more in the typographic scale basics article.
In order to create fluid typography, you can use the clamp() CSS function to clamp the value of font-size between a minimum and maximum value. This ensures that the text size will never be smaller than the minimum value or larger than the maximum value.
It is advised to use rem units for the minimum and maximum values, as they are relative to the root font size. This allows you to set a base font size on the html element and have the text scale accordingly.
Finally, you should use a formula to calculate the responsive value for font-size. This formula should be based on the viewport width, so that the text size grows or shrinks as the viewport changes, thus you can use the vw unit.
In the example below, the font-size is clamped between 1rem and 3rem, and the formula 8vw - 2rem is used to calculate the responsive value. This formula ensures that the text size will be 1rem at 600px viewport width and 3rem at 1000px viewport width.
.fluid-type {
font-size: clamp(1rem, 8vw - 2rem, 3rem);
}Snippets Css S Footer At The Bottom (`content/snippets/css/s/footer-at-the-bottom.md`)
title: How can I ensure the footer is always at the bottom of the page? shortTitle: Footer at the bottom language: css tags: [layout] cover: flower-shape-sunset excerpt: Make sure the footer stays at the bottom of the page, instead of floating up when the content is too short. listed: true dateModified: 2022-10-30
Preventing the footer from floating up the page is important when trying to create a polished website. Pages with short content can run into this issue, but it's easy to fix with a few lines of CSS. Assuming your HTML looks something like the snippet below, here are two modern ways to ensure the footer is always at the bottom of the page:
<body>
<main></main>
<footer></footer>
</body>Using Flexbox
You can use flexbox to ensure that the footer is always at the bottom of the page. This is done by setting the giving the body element min-height: 100dvh, display: flex and flex-direction: column. If you need to support older browsers, you can also add a fallback for min-height using 100vh.
Then, give the footer element a margin-top: auto to make its margin fill the remaining space between it and its previous sibling. Note that this technique will not stretch the previous sibling, but rather push the footer to the bottom of the page.
body {
/* Fallback for browsers that don't support dvh */
min-height: 100vh;
min-height: 100dvh;
display: flex;
flex-direction: column;
}
footer {
margin-top: auto;
}Using Grid
You can also use grid in a very similar fashion. Simply swap display: flex for display: grid and flex-direction: column for grid-template-rows: 1fr auto in the body element.
No additional attributes are needed for the footer element. In this case, the fr unit is leveraged to stretch the main element to fill the remaining space.
body {
/* Fallback for browsers that don't support dvh */
min-height: 100vh;
min-height: 100dvh;
display: grid;
grid-template-rows: 1fr auto;
}Notes
As you can see, both techniques are straightforward to implement. Depending on your needs one might be more suitable than the other. Generally speaking, grid is more flexible in most cases and can help if you have more complex layouts, which can include a header or sidebar.
Snippets Css S Fullscreen (`content/snippets/css/s/fullscreen.md`)
title: Style elements when in fullscreen mode shortTitle: Fullscreen element styling language: css tags: [visual] cover: flower-portrait-3 excerpt: Did you know you can differentiate an element's styles when it's in fullscreen mode? Learn how to do it with this code snippet! listed: true dateModified: 2024-09-08
Did you know you can differentiate an element's styles when it's in fullscreen mode? It doesn't even require special classes or JavaScript, as you can use the :fullscreen pseudo-class to style an element when it's displayed in fullscreen mode.
This pseudo-class is particularly useful in adjusting an element's appearance when it's in fullscreen mode. Perhaps, you want to adjust the text size, background color, or even the layout of the element to better fit the screen. This can be especially useful for media players, slideshows, or any other content that benefits from a fullscreen view.
https://codepen.io/chalarangelo/pen/bGXpEoP
.element {
/* Default styles */
}
.element:fullscreen {
/* Styles when in fullscreen mode */
}[!NOTE]
You can learn more about requesting fullscreen access in the related JavaScript article. The gist of it is that you can use
Element.requestFullscreen()to make an element fullscreen andDocument.exitFullscreen()to exit fullscreen mode.
Snippets Css S Gradient Text (`content/snippets/css/s/gradient-text.md`)
title: Gradient text language: css tags: [visual] cover: red-berries excerpt: Modern CSS can help you create gradient text with a few lines of code. Learn how today! listed: true dateModified: 2024-09-10
I remember a time when gradients were tricky to use for backgrounds, let alone for styling text. But, with modern CSS, it's pretty easy to create gradient text with only a few lines of code, at least on WebKit browsers (Chrome, Edge, Safari).
Styling text with gradients starts the same as creating a gradient for the background. You use the linear-gradient() function to define the gradient colors and direction.
Then, you have to apply it to the text using the -webkit-text-fill-color property with a value of transparent. Finally, clip the background with the text using the background-clip property with a value of text and you're done!
.gradient-text {
background: linear-gradient(to right, #009FFF, #ec2F4B);
-webkit-text-fill-color: transparent;
background-clip: text;
-webkit-background-clip: text;
}https://codepen.io/chalarangelo/pen/gOVrreP
Snippets Css S Hide Scrollbars (`content/snippets/css/s/hide-scrollbars.md`)
title: Hide scroll bars on an element shortTitle: Hide scrollbars language: css tags: [visual] cover: by-the-lighthouse excerpt: Learn how to hide scrollbars on an element, while still allowing it to be scrollable. listed: true dateModified: 2024-09-07
Scrollbars are a common pain point when styling pages, mainly due to a lack of customization options and inconsistencies between browsers. You can hide scrollbars on an element while still allowing it to be scrollable by using the overflow property and some vendor-specific properties.
First off, you need to ensure your content remains scrollable, by setting overflow: auto on the element. Then, you can hide the scrollbars using scrollbar-width: none on Firefox and display: none on the ::-webkit-scrollbar pseudo-element on WebKit browsers (Chrome, Edge, Safari).
.no-scrollbars {
overflow: auto;
scrollbar-width: none;
}
.no-scrollbars::-webkit-scrollbar {
display: none;
}Snippets Css S Horizontal Vertical Gallery (`content/snippets/css/s/horizontal-vertical-gallery.md`)
title: Image gallery with horizontal or vertical scroll shortTitle: Image gallery language: css tags: [visual,interactivity] cover: flower-portrait-5 excerpt: Create a horizontally or vertically scrollable image gallery. listed: true dateModified: 2024-09-05
Image galleries are useful in various contexts, from showcasing products to displaying a collection of images. Depending on your needs, you may want to create a horizontally or vertically scrollable gallery. While CSS has come a long way, unfortunately, you'll have to get your hands dirty with JavaScript to achieve this effect.
[!NOTE]
You may first want to get caught up with scroll snapping to understand how it works, if you haven't already.
Image gallery with vertical scroll
To create a vertically scrollable image gallery, you will need to create a container with display: flex and justify-content: center and a set of slides with display: flex and flex-direction: column.
You will also need to use scroll-snap-type: y mandatory and overscroll-behavior-y: contain to create a snap effect on vertical scroll. Snap elements to the start of the container using scroll-snap-align: start. In order to hide the scrollbars, you can use scrollbar-width: none and style the pseudo-element ::-webkit-scrollbar to display: none.
Then, you can use Element.scrollTo() to define a scrollToElement function that scrolls the gallery to the given item. You can use Array.prototype.map() and Array.prototype.join() to populate the .thumbnails element. Give each thumbnail a data-id attribute with the index of the image.
Using the Document.querySelectorAll() method, you can get all the thumbnail elements. Use Array.prototype.forEach() to register a handler for the 'click' event on each thumbnail, using EventTarget.addEventListener() and the scrollToElement function.
Finally, use Document.querySelector() and EventTarget.addEventListener() to register a handler for the 'scroll' event. Update the .thumbnails and .scrollbar elements to match the current scroll position, using the scrollThumb function.
https://codepen.io/chalarangelo/pen/ExqVMzW
<div class="gallery-container">
<div class="thumbnails"></div>
<div class="scrollbar">
<div class="thumb"></div>
</div>
<div class="slides">
<div><img src="https://picsum.photos/id/1067/540/720"></div>
<div><img src="https://picsum.photos/id/122/540/720"></div>
<div><img src="https://picsum.photos/id/188/540/720"></div>
<div><img src="https://picsum.photos/id/249/540/720"></div>
<div><img src="https://picsum.photos/id/257/540/720"></div>
<div><img src="https://picsum.photos/id/259/540/720"></div>
<div><img src="https://picsum.photos/id/283/540/720"></div>
<div><img src="https://picsum.photos/id/288/540/720"></div>
<div><img src="https://picsum.photos/id/299/540/720"></div>
</div>
</div>.gallery-container {
display: flex;
justify-content: center;
}
.thumbnails {
display: flex;
flex-direction: column;
gap: 8px;
}
.thumbnails img {
width: 40px;
height: 40px;
cursor: pointer;
}
.scrollbar {
width: 1px;
height: 720px;
background: #ccc;
display: block;
margin: 0 0 0 8px;
}
.thumb {
width: 1px;
position: absolute;
height: 0;
background: #000;
}
.slides {
margin: 0 16px;
display: grid;
grid-auto-flow: row;
gap: 1rem;
width: calc(540px + 1rem);
padding: 0 0.25rem;
height: 720px;
overflow-y: auto;
overscroll-behavior-y: contain;
scroll-snap-type: y mandatory;
scrollbar-width: none;
}
.slides > div {
scroll-snap-align: start;
}
.slides img {
width: 540px;
object-fit: contain;
}
.slides::-webkit-scrollbar {
display: none;
}const slideGallery = document.querySelector('.slides');
const slides = slideGallery.querySelectorAll('div');
const scrollbarThumb = document.querySelector('.thumb');
const slideCount = slides.length;
const slideHeight = 720;
const marginTop = 16;
const scrollThumb = () => {
const index = Math.floor(slideGallery.scrollTop / slideHeight);
scrollbarThumb.style.height = `${((index + 1) / slideCount) * slideHeight}px`;
};
const scrollToElement = el => {
const index = parseInt(el.dataset.id, 10);
slideGallery.scrollTo(0, index * slideHeight + marginTop);
};
document.querySelector('.thumbnails').innerHTML += [...slides]
.map(
(slide, i) => `<img src="${slide.querySelector('img').src}" data-id="${i}">`
)
.join('');
document.querySelectorAll('.thumbnails img').forEach(el => {
el.addEventListener('click', () => scrollToElement(el));
});
slideGallery.addEventListener('scroll', e => scrollThumb());
scrollThumb();Image gallery with horizontal scroll
To create a horizontally scrollable image gallery, you will need to position its .thumbnails container at the bottom of the gallery, using position: absolute. Then, use scroll-snap-type: x mandatory and overscroll-behavior-x: contain to create a snap effect on horizontal scroll. Snap elements to the start of the container using scroll-snap-align: start.
Hide the scrollbars the same way as before. Use Element.scrollTo() to define a scrollToElement function that scrolls the gallery to the given item. Populate the .thumbnails element using Array.prototype.map() and Array.prototype.join(), giving each thumbnail a data-id attribute with the index of the image.
Use Document.querySelectorAll() to get all the thumbnail elements and register 'click' event handlers on each thumbnail, using the highlightThumbnail function. Finally, register a handler for the 'scroll' event the same as before and update the .thumbnails element to match the current scroll position using the highlightThumbnail function.
https://codepen.io/chalarangelo/pen/RwXWObY
<div class="gallery-container">
<div class="thumbnails"></div>
<div class="slides">
<div><img src="https://picsum.photos/id/1067/540/720"></div>
<div><img src="https://picsum.photos/id/122/540/720"></div>
<div><img src="https://picsum.photos/id/188/540/720"></div>
<div><img src="https://picsum.photos/id/249/540/720"></div>
<div><img src="https://picsum.photos/id/257/540/720"></div>
<div><img src="https://picsum.photos/id/259/540/720"></div>
<div><img src="https://picsum.photos/id/283/540/720"></div>
<div><img src="https://picsum.photos/id/288/540/720"></div>
<div><img src="https://picsum.photos/id/299/540/720"></div>
</div>
</div>.gallery-container {
position: relative;
display: flex;
justify-content: center;
}
.thumbnails {
position: absolute;
bottom: 8px;
display: flex;
flex-direction: row;
gap: 6px;
}
.thumbnails div {
width: 8px;
height: 8px;
cursor: pointer;
background: #aaa;
border-radius: 100%;
}
.thumbnails div.highlighted {
background-color: #777;
}
.slides {
margin: 0 16px;
display: grid;
grid-auto-flow: column;
gap: 1rem;
width: 540px;
padding: 0 0.25rem;
height: 720px;
overflow-y: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
scrollbar-width: none;
}
.slides > div {
scroll-snap-align: start;
}
.slides img {
width: 540px;
object-fit: contain;
}
.slides::-webkit-scrollbar {
display: none;
}const slideGallery = document.querySelector('.slides');
const slides = slideGallery.querySelectorAll('div');
const thumbnailContainer = document.querySelector('.thumbnails');
const slideCount = slides.length;
const slideWidth = 540;
const highlightThumbnail = () => {
thumbnailContainer
.querySelectorAll('div.highlighted')
.forEach(el => el.classList.remove('highlighted'));
const index = Math.floor(slideGallery.scrollLeft / slideWidth);
thumbnailContainer
.querySelector(`div[data-id="${index}"]`)
.classList.add('highlighted');
};
const scrollToElement = el => {
const index = parseInt(el.dataset.id, 10);
slideGallery.scrollTo(index * slideWidth, 0);
};
thumbnailContainer.innerHTML += [...slides]
.map((slide, i) => `<div data-id="${i}"></div>`)
.join('');
thumbnailContainer.querySelectorAll('div').forEach(el => {
el.addEventListener('click', () => scrollToElement(el));
});
slideGallery.addEventListener('scroll', e => highlightThumbnail());
highlightThumbnail();Snippets Css S Hover Additional Content (`content/snippets/css/s/hover-additional-content.md`)
title: Reveal additional content on hover language: css tags: [visual] cover: dark-leaves-5 excerpt: Learn how you can create a card that displays additional content on hover. listed: true dateModified: 2024-09-23
Fairly often, I've found myself needing to display additional content on hover. A while ago, I stumbled upon a pretty cool effect, where you can push the image and title of a card upwards to reveal more content. Achieving this effect is pretty simple, too.
The trick is using overflow: hidden on the card to hide elements that overflow vertically. Then, you can use the :hover and :focus-within pseudo-class selectors to change the card's styling if the element is hovered, focused, or any of its descendants are focused.
Then, use a transition to create a transition effect. Using the margin and padding properties, you can push the image and title upwards to reveal additional content.
https://codepen.io/chalarangelo/pen/NWQNOmj
<div class="card">
<img src="https://picsum.photos/id/404/367/267"/>
<h3>Lorem ipsum</h3>
<div class="focus-content">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br/> <a href="#">Link to source</a>
</p>
</div>
</div>.card {
width: 300px;
height: 280px;
padding: 0;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.1);
border-radius: 8px;
box-sizing: border-box;
overflow: hidden;
}
.card * {
transition: 0.3s ease all;
}
.card img {
margin: 0;
width: 300px;
height: 224px;
object-fit: cover;
display: block;
}
.card h3 {
margin: 0;
padding: 12px 12px 48px;
line-height: 32px;
font-weight: 500;
font-size: 20px;
}
.card .focus-content {
display: block;
padding: 8px 12px;
}
.card p {
margin: 0;
line-height: 1.5;
}
.card:hover img, .card:focus-within img {
margin-top: -80px;
}
.card:hover h3, .card:focus-within h3 {
padding: 8px 12px 0;
}Snippets Css S Hover Shadow Box Animation (`content/snippets/css/s/hover-shadow-box-animation.md`)
title: Animate box shadow on hover language: css tags: [animation] cover: dark-cloud excerpt: Create an animated shadow box around the text when it is hovered. listed: true dateModified: 2024-09-18
One of material design's coolest tricks is the illusion of depth created by shadows. As part of hover interactions, elements are often attracted to your finger, as if they were floating above the surface. Luckily, it's a pretty easy effect to achieve with CSS.
The key to this effect is using transform with the perspective() function to create a 3D space for the element, by affecting the distance between the Z plane and the user. Then, using the translate() function, you can reposition the element along z-axis in 3D space.
The leftover work is using box-shadow to create a shadow around the element and then, using :hover, :active, and :focus pseudo-class selectors, to apply a new box-shadow and transform to the element.
https://codepen.io/chalarangelo/pen/xxvVyoB
.hover-shadow-box-animation {
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px transparent;
transition-duration: 0.3s;
transition-property: box-shadow, transform;
}
.hover-shadow-box-animation:hover,
.hover-shadow-box-animation:focus,
.hover-shadow-box-animation:active {
box-shadow: 1px 10px 10px -10px rgba(0, 0, 24, 0.5);
transform: scale(1.2);
}Snippets Css S Hover Underline Animation (`content/snippets/css/s/hover-underline-animation.md`)
title: Hover underline animation language: css tags: [animation] cover: coffee-phone-tray-2 excerpt: Create an animated underline effect when the user hovers over some text. listed: true dateModified: 2024-08-27
One of the fanciest text effects I've seen is an animated underline, where the underline appears to grow from the left side of the text when the user hovers over it. This effect can be achieved using CSS and the ::after pseudo-element. But first, let me show you what it looks like:
https://codepen.io/chalarangelo/pen/KKOpeae
So, how does one implement this effect? Like I said, we'll make use of the ::after pseudo-element to create the underline. It will need to have a width spanning the entire parent element and position: absolute to place it below the text content.
Naturally, the parent must then use display: inline-block to make the underline span just the width of the text content and position: relative to position the pseudo-element correctly.
Initially, we'll hide the pseudo-element using transform: scaleX(0) and show it on hover using transform: scaleX(1). To complete the effect, we'll add a transition to the transform property and adjust the transform-origin to make the underline appear to grow from the left side of the text.
You can adjust the color, size, and other properties to fit your design. The most notable change you can make is the transform-origin. Reversing the values will make the underline appear to grow from the right side of the text instead. Removing the transform-origin property will make the underline grow from the center of the text.
.hover-underline-animation {
display: inline-block;
position: relative;
}
.hover-underline-animation::after {
content: '';
position: absolute;
width: 100%;
transform: scaleX(0);
height: 2px;
bottom: 0;
left: 0;
background-color: #0087ca;
transform-origin: bottom right;
transition: transform 0.25s ease-out;
}
.hover-underline-animation:hover::after {
transform: scaleX(1);
transform-origin: bottom left;
}Snippets Css S Image Hover Menu (`content/snippets/css/s/image-hover-menu.md`)
title: Show menu on image hover language: css tags: [layout,animation] cover: compass excerpt: Display a menu overlay when the user hovers over an image. listed: true dateModified: 2024-09-24
To display a menu overlay when the user hovers over an image, you can use a combination of HTML and CSS techniques. Start by using a <figure> element to wrap both the `` element and a <nav> element that will contain the menu links. This structure allows you to control the layout and positioning of the image and the menu.
To create a sliding effect, use the opacity and right attributes to animate the image on hover. By adjusting these properties, you can make the image appear to slide to the side, revealing the menu. Additionally, set the left attribute of the <nav> to the negative of the element's width. When hovering over the parent element, reset the left attribute to slide the menu into view.
For the menu items, use display: flex, flex-direction: column, and justify-content: center on the <nav> to vertically center them. This ensures that the menu items are evenly spaced and aligned in the center of the menu overlay.
https://codepen.io/chalarangelo/pen/zYgqmQM
<figure class="hover-menu">
<img src="https://picsum.photos/id/1060/800/480.jpg"/>
<nav>
<a href="#">Home</a>
<a href="#">Pricing</a>
<a href="#">About</a>
</nav>
</figure>.hover-menu {
position: relative;
overflow: hidden;
margin: 8px;
min-width: 340px;
max-width: 480px;
max-height: 290px;
width: 100%;
background: #000;
text-align: center;
box-sizing: border-box;
}
.hover-menu * {
box-sizing: border-box;
}
.hover-menu img {
position: relative;
max-width: 100%;
top: 0;
right: 0;
opacity: 1;
transition: 0.3s ease-in-out;
}
.hover-menu nav {
position: absolute;
top: 0;
left: -120px;
width: 120px;
height: 100%;
background: #000;
transition: 0.3s ease-in-out;
display: flex;
flex-direction: column;
justify-content: center;
}
.hover-menu nav a {
display: block;
color: #fff;
opacity: 0.8;
position: relative;
transition: 0.3s ease-in-out;
}
.hover-menu:hover img {
opacity: 0.5;
right: -120px;
}
.hover-menu:hover nav {
left: 0;
opacity: 1;
}Snippets Css S Image Hover Rotate (`content/snippets/css/s/image-hover-rotate.md`)
title: Image rotate on hover language: css tags: [animation,visual] cover: succulent-1 excerpt: Zoom in and rotate your images on hover and make them stand out. listed: true dateModified: 2024-09-21
An extremely cool and modern effect is to rotate and zoom in on an image when hovering over it. All it takes is a container (e.g. a <figure>) with an image inside and a few lines of CSS.
To achieve the desired effect, simply use the scale() and rotate() functions in the transform property of the image. Then, use the transition property to animate the transformation when hovering over the parent element.
Add overflow: hidden to the parent element to hide the excess from the image transformation. You can also set a min-width, max-width, and width to control the size of the container.
https://codepen.io/chalarangelo/pen/BaXKqep
<figure class="hover-rotate">
<img src="/path/to/your/img.jpg"/>
</figure>.hover-rotate {
overflow: hidden;
min-width: 240px;
max-width: 320px;
width: 100%;
}
.hover-rotate img {
transition: all 0.3s;
box-sizing: border-box;
max-width: 100%;
}
.hover-rotate:hover img {
transform: scale(1.3) rotate(5deg);
}Snippets Css S Image Mosaic (`content/snippets/css/s/image-mosaic.md`)
title: Responsive image mosaic language: css tags: [layout] cover: beach-riders excerpt: Create a responsive image mosaic, using CSS grid to create a grid layout and media queries to adjust the layout for different screen sizes. listed: true dateModified: 2024-09-03
CSS grid is one of the most powerful tools in modern web design. It allows you to create complex layouts with ease, making it perfect for creating image mosaics.
By using display: grid, you can create a responsive grid layout that adapts to different screen sizes. You can also use grid-row: span 2 / auto and grid-column: span 2 / auto to create items that span two rows or two columns respectively. Finally, you can wrap these styles in a media query to avoid applying them on small screen sizes, making sure your layout looks great on all devices.
https://codepen.io/chalarangelo/pen/XWvbQjz
.image-mosaic {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
grid-auto-rows: 240px;
}
@media screen and (min-width: 600px) {
.card-tall {
grid-row: span 2 / auto;
}
.card-wide {
grid-column: span 2 / auto;
}
}Snippets Css S Image Overlay Hover (`content/snippets/css/s/image-overlay-hover.md`)
title: Image overlay on hover language: css tags: [visual,animation] cover: architectural excerpt: Learn how to display an image overlay effect on hover using CSS. listed: true dateModified: 2024-09-20
Creating an image overlay effect on hover is a great way to add a touch of interactivity to your website. This effect can be achieved using pseudo-elements and pseudo-selectors. For the structure, you'll use a <figure> element containing an `` and a <figcaption>.
Using the ::before and ::after pseudo-elements, you can create the top and bottom bars of your overlay. Set their opacity, transform, and transition to produce the desired effect.
The <figcaption> element can be used for the text of the overlay. By setting display: flex, flex-direction: column, and justify-content: center, you can center the text into the image.
Finally, by using the :hover pseudo-selector, you can update the opacity and transform of all the elements and display the overlay.
https://codepen.io/chalarangelo/pen/bGXpmJq
<figure class="hover-img">
<img src="https://picsum.photos/id/200/440/320.jpg"/>
<figcaption>
<h3>Lorem <br/>Ipsum</h3>
</figcaption>
</figure>.hover-img {
background-color: #000;
color: #fff;
display: inline-block;
margin: 8px;
max-width: 320px;
min-width: 240px;
overflow: hidden;
position: relative;
text-align: center;
width: 100%;
}
.hover-img * {
box-sizing: border-box;
transition: all 0.45s ease;
}
.hover-img::before,
.hover-img::after {
background-color: rgba(0, 0, 0, 0.5);
border-top: 32px solid rgba(0, 0, 0, 0.5);
border-bottom: 32px solid rgba(0, 0, 0, 0.5);
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
content: '';
transition: all 0.3s ease;
z-index: 1;
opacity: 0;
transform: scaleY(2);
}
.hover-img img {
vertical-align: top;
max-width: 100%;
backface-visibility: hidden;
}
.hover-img figcaption {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
align-items: center;
z-index: 1;
display: flex;
flex-direction: column;
justify-content: center;
line-height: 1.1em;
opacity: 0;
z-index: 2;
transition-delay: 0.1s;
font-size: 24px;
font-family: sans-serif;
font-weight: 400;
letter-spacing: 1px;
text-transform: uppercase;
}
.hover-img:hover::before,
.hover-img:hover::after {
transform: scale(1);
opacity: 1;
}
.hover-img:hover > img {
opacity: 0.7;
}
.hover-img:hover figcaption {
opacity: 1;
}Snippets Css S Image Text Overlay (`content/snippets/css/s/image-text-overlay.md`)
title: Image with text overlay language: css tags: [visual] cover: icebreaker excerpt: Learn how to create an image with a text overlay using CSS. listed: true dateModified: 2024-09-08
Displaying text on top of images, while very common, is fairly tricky to get right. This is mainly because the text needs to be readable regardless of the background image and color. Luckily, creating text overlays is easy with CSS.
https://codepen.io/chalarangelo/pen/WNVwQgY
Using `backdrop-filter`
The most modern technique to create a text overlay is by using the backdrop-filter property. This property applies a filter to the backdrop of an element, which is the area behind the element itself.
For an overlay effect, you can use the blur() and brightness() functions, which apply a blur and adjust the brightness of the backdrop respectively.
<figure>
<figcaption>Text</figcaption>
<img src="/path/to/img.jpg">
</figure>figure {
position: relative;
}
figcaption {
position: absolute;
color: #fff;
backdrop-filter: blur(14px) brightness(75%);
}Using `linear-gradient`
A more traditional way to achieve a similar effect is by using a linear-gradient() background. This method is less flexible than backdrop-filter, but it works well in most cases.
Depending on your needs, you can adjust the gradient's colors, direction, and opacity to create different overlay effects.
<figure>
<img src="/path/to/img.jpg">
<figcaption>Text</figcaption>
</figure>figure {
position: relative;
}
figcaption {
position: absolute;
color: #fff;
background: linear-gradient(0deg, #00000088 30%, #ffffff44 100%);
}Snippets Css S Inherited Properties Cheatsheet (`content/snippets/css/s/inherited-properties-cheatsheet.md`)
title: CSS inherited properties cheatsheet shortTitle: Inherited properties language: css tags: [layout,cheatsheet] cover: half-trees excerpt: A quick reference for inherited CSS properties. listed: true dateModified: 2022-11-20
The CSS specification clearly states which CSS properties are inherited but the related appendix is not the most user-friendly resource. Here's a quick reference of the inherited properties that you are most likely to run into:
border-collapseborder-spacingcaption-sidecolorcursordirectionempty-cellsfont-familyfont-sizefont-stylefont-variantfont-weightfont-size-adjustfont-stretchfontletter-spacingline-heightlist-style-imagelist-style-positionlist-style-typelist-styleorphansquotestab-sizetext-aligntext-align-lasttext-decoration-colortext-indenttext-justifytext-shadowtext-transformvisibilitywhite-spacewidowsword-breakword-spacingword-wrap
Snippets Css S Input With Prefix (`content/snippets/css/s/input-with-prefix.md`)
title: Input with prefix language: css tags: [interactivity,visual] cover: flower-portrait-4 excerpt: Ever wanted to create an input with a visual, non-editable prefix? CSS can help you with that! listed: true dateModified: 2024-08-30
Ever wanted to create an input with a visual, non-editable prefix? HTML, CSS and some ingenuity can help you with that!
All you really need is two elements: a for the prefix and an for the actual input field. By styling them accordingly, you can create a visually appealing input box that looks like it has a prefix.
For starters, you can create a container element with display: flex to align the prefix and the input field. You can then remove the border and outline from the `` field. Apply them to the parent element instead to make it look like an input box.
Finally, you can use the :focus-within pseudo-class selector to style the parent element accordingly, when the user interacts with the `` field.
<div class="input-box">
<span class="prefix">+30</span>
<input type="tel" placeholder="210 123 4567"/>
</div>.input-box {
display: flex;
align-items: center;
border: 1px solid #a0a0a0;
border-radius: 4px;
}
.input-box input {
border: none;
outline: none;
}
.input-box:focus-within {
border-color: #4f7df3;
}https://codepen.io/chalarangelo/pen/gOVpEMK
[!CAUTION]
Make sure to include your prefix when submitting the form. You can use JavaScript to append it to the input value before sending it to the server.
Snippets Css S Inset (`content/snippets/css/s/inset.md`)
title: Using the inset CSS property shortTitle: Using inset language: css tags: [layout] cover: yellow-white-mug-1 excerpt: The inset shorthand property makes element positioning in CSS easier. Learn how to use it effectively in your projects. listed: true dateModified: 2025-05-15
A few months back, I learned about the inset property in CSS and was pleasantly surprised by it. I still haven't seen it in the wild all that much, so I thought I'd take a moment to share some thoughts on it and why I think it's a pretty useful tool to have in your CSS toolbox.
What is the `inset` property?
The inset property is a shorthand for top, right, bottom, and left. This can save you from the tedious process of defining these properties individually, as you can use inset to set them all at once.
<div class="box"></div>.box {
position: absolute;
/* top: 10px, right: 20px, bottom: 30px, left: 40px */
inset: 10px 20px 30px 40px;
}Physical, not logical
Although the inset property is defined in the CSS Logical Properties and Values Module, it is a physical property, not a logical one. This means it always refers to the physical dimensions (top, right, bottom, left) of an element, regardless of the writing mode or text direction (e.g., left-to-right or right-to-left).
If you need logical positioning (e.g., inline-start, block-end), you'll need to use the logical properties instead.
Positioned elements
One important thing to remember is that the inset property only works on positioned elements. This means the element must have a position value of absolute, relative, fixed, or sticky. Without a position value, the inset property will have no effect.
<div class="box"></div>.box {
/* The `position` is required for `inset` to work */
position: absolute;
/* top: 20px, right: 20px, bottom: 20px, left: 20px */
inset: 20px;
}Practical use cases
Ok, that's a cool shorthand. But what can I do with it? Here are a few practical use cases for the inset property:
Fill a parent
If you want an element to fill its parent container, you can simply use inset: 0 to set all sides to 0. This is a concise way to set top, right, bottom, and left to 0.
<div class="parent">
<div class="child"></div>
</div>.parent {
position: relative;
}
.child {
position: absolute;
/* Fill the parent */
/* top: 0, right: 0, bottom: 0, left: 0 */
inset: 0;
}Center an element
To center an element within its parent, you can use one or more values of the inset property to position it. If, for example, the element is half the size of its parent, you can use inset: 25% to center it.
<div class="parent">
<div class="child"></div>
</div>.parent {
position: relative;
width: 200px;
height: 200px;
}
.child {
position: absolute;
/* Center the child */
/* top: 25%, right: 25%, bottom: 25%, left: 25% */
inset: 25%;
width: 100px;
height: 100px;
}Custom positioning
You can also use inset with custom values to position an element in a specific way, leveraging the auto keyword to essentially skip some sides. For example, if you want to position an element 10px from the top and 50px from the left, you can skip the two values in the middle.
<div class="box"></div>.box {
position: absolute;
/* top: 10px, left: 50px */
inset: 10px auto auto 50px;
}Conclusion
The inset property is a handy shorthand for positioning elements in CSS. By combining it with the appropriate position value, you can simplify your code and achieve a variety of layouts, from filling a parent container to centering elements or creating custom positions. Just keep in mind that it is a physical property, so it won't adapt to writing modes or text directions.
Snippets Css S Isometric Card (`content/snippets/css/s/isometric-card.md`)
title: Isometric card language: css tags: [visual] cover: guitar-living-room excerpt: Learn how to create an isometric card using CSS. listed: true dateModified: 2024-09-10
The isometric perspective is a type of axonometric projection that represents three-dimensional objects in two dimensions. It harkens back to the days of early video games and is a fun way to create a unique visual effect on the web.
All you need to do is use transform with rotateX() and rotateY() to create the isometric effect. Using transform-style: preserve-3d ensures that the card maintains its 3D perspective. Moreover, you can add will-change: transform to optimize the card for animations.
You can also add a box-shadow to give the card a more realistic look. Finally, use transition to animate the card, creating a lift effect when the user hovers over it.
https://codepen.io/chalarangelo/pen/BaXKKpj
.isometric-card {
transform: rotateX(51deg) rotateZ(43deg);
transform-style: preserve-3d;
will-change: transform;
box-shadow: 1px 1px 0 1px #f9f9fb, -1px 0 28px 0 rgba(34, 33, 81, 0.01),
28px 28px 28px 0 rgba(34, 33, 81, 0.25);
transition: 0.4s ease-in-out transform, 0.3s ease-in-out box-shadow;
}
.isometric-card:hover {
transform: translate3d(0px, -16px, 0px) rotateX(51deg) rotateZ(43deg);
box-shadow: 1px 1px 0 1px #f9f9fb, -1px 0 28px 0 rgba(34, 33, 81, 0.01),
54px 54px 28px -10px rgba(34, 33, 81, 0.15);
}Snippets Css S Loaders (`content/snippets/css/s/loaders.md`)
title: Animated CSS loaders shortTitle: Animated loaders language: css tags: [animation] cover: digital-nomad-12 excerpt: Loading indicators are a staple of modern web design. Here are some CSS loaders to keep your users engaged while they wait. listed: true dateModified: 2024-09-02
Loading indicators are a staple of modern web design. They keep users engaged while they wait for content to load. With a little HTML and CSS, you can create a variety of loaders to suit your needs. Here are some examples to get you started.
https://codepen.io/chalarangelo/pen/LYwVvVr
Bouncing ball loader
For the bouncing loader, you'll need a parent with three elements, one for each ball. Use @keyframes to define a bouncing animation, using the opacity and transform properties. Use a single axis translation on transform: translate3d() to achieve better animation performance.
For the container, you'll have to set display: flex and justify-content: center to position the balls in the center. Give each ball the same width and height and border-radius: 50% to make them circular. Apply the animation to each ball, using a different animation-delay for each and animation-direction: alternate to create the appropriate effect.
<div class="bouncing-loader">
<div></div>
<div></div>
<div></div>
</div>.bouncing-loader {
display: flex;
justify-content: center;
}
.bouncing-loader > div {
width: 16px;
height: 16px;
margin: 3rem 0.2rem;
background: #8385aa;
border-radius: 50%;
animation: bouncing-loader 0.6s infinite alternate;
}
.bouncing-loader > div:nth-child(2) {
animation-delay: 0.2s;
}
.bouncing-loader > div:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bouncing-loader {
to {
opacity: 0.1;
transform: translate3d(0, -16px, 0);
}
}Pulse loader
For the pulse loader, you'll need a parent container with two child elements, one for each ring. Use @keyframes to define an animation at two points in the cycle. At the start (0%), the two child elements have no width or height and are positioned at the center. At the end (100%), both elements have increased width and height, but their position is reset to 0. Use opacity to transition from 1 to 0 when animating to give them a disappearing effect as they expand.
Set a predefined width and height for the parent container and use position: relative to position its children. Use animation-delay on the second child, so that each element starts its animation at a different time.
<div class="ripple-loader">
<div></div>
<div></div>
</div>.ripple-loader {
position: relative;
width: 64px;
height: 64px;
}
.ripple-loader div {
position: absolute;
border: 4px solid #454ADE;
border-radius: 50%;
animation: ripple-loader 1s ease-out infinite;
}
.ripple-loader div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes ripple-loader {
0% {
top: 32px;
left: 32px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0;
left: 0;
width: 64px;
height: 64px;
opacity: 0;
}
}Donut spinner
For the donut spinner, you'll need a single element. Use a semi-transparent border for the whole element. Exclude one side that will serve as the loading indicator for the donut. Define and use an appropriate animation, using transform: rotate() to rotate the element.
<div class="donut"></div>.donut {
display: inline-block;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #7983ff;
border-radius: 50%;
width: 30px;
height: 30px;
animation: donut-spin 1.2s linear infinite;
}
@keyframes donut-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}Snippets Css S Logical Physical Properties Map (`content/snippets/css/s/logical-physical-properties-map.md`)
title: Logical and physical CSS properties equivalents shortTitle: Logical & physical properties language: css tags: [layout] cover: succulent-8 excerpt: New to CSS logical properties? This article provides a handy map of logical properties to their physical counterparts. listed: true dateModified: 2025-05-12
Logical properties are a new way to define layout in CSS. They allow you to use logical directions (like start, end, inline, and block) instead of physical directions (like left, right, top, and bottom). This makes it easier to create layouts that work well in different writing modes and languages.
However, if you're used to working with physical properties, it can be a bit confusing at first. To help you out, here's a handy map of logical properties to their physical counterparts!
[!NOTE]
In this article, I'm assuming you're writing from left to right (LTR). If you're writing from right to left (RTL), the physical properties will be reversed. For example,
leftbecomesright, andtopbecomesbottom.
Position
| Logical Property | Physical Property (LTR) |
|---|---|
inset-inline-start |
left |
inset-inline-end |
right |
inset-inline |
left & right |
inset-block-start |
top |
inset-block-end |
bottom |
inset-block |
top & bottom |
Margin
| Logical Property | Physical Property (LTR) |
|---|---|
margin-inline-start |
margin-left |
margin-inline-end |
margin-right |
margin-inline |
margin-left & margin-right |
margin-block-start |
margin-top |
margin-block-end |
margin-bottom |
margin-block |
margin-top & margin-bottom |
Padding
| Logical Property | Physical Property (LTR) |
|---|---|
padding-inline-start |
padding-left |
padding-inline-end |
padding-right |
padding-inline |
padding-left & padding-right |
padding-block-start |
padding-top |
padding-block-end |
padding-bottom |
padding-block |
padding-top & padding-bottom |
Border
| Logical Property | Physical Property (LTR) |
|---|---|
border-inline-start |
border-left |
border-inline-start-color |
border-left-color |
border-inline-start-style |
border-left-color |
border-inline-start-width |
border-left-color |
border-inline-end |
border-right |
border-inline-end-color |
border-right-color |
border-inline-end-style |
border-right-color |
border-inline-end-width |
border-right-color |
border-inline |
border-left & border-right |
border-inline-color |
border-left-color & border-right-color |
border-inline-style |
border-left-style & border-right-style |
border-inline-width |
border-left-width & border-right-width |
border-block-start |
border-top |
border-block-start-color |
border-top-color |
border-block-start-style |
border-top-color |
border-block-start-width |
border-top-color |
border-block-end |
border-bottom |
border-block-end-color |
border-bottom-color |
border-block-end-style |
border-bottom-color |
border-block-end-width |
border-bottom-color |
border-block |
border-top & border-bottom |
border-block-color |
border-top-color & border-bottom-color |
border-block-style |
border-top-style & border-bottom-style |
border-block-width |
border-top-width & border-bottom-width |
border-start-start-radius |
border-top-left-radius |
border-start-end-radius |
border-top-right-radius |
border-end-start-radius |
border-bottom-left-radius |
border-end-end-radius |
border-bottom-right-radius |
Size
| Logical Property | Physical Property (LTR) |
|---|---|
inline-size |
width |
min-inline-size |
min-width |
max-inline-size |
max-width |
block-size |
height |
min-block-size |
min-height |
max-block-size |
max-height |
Values
In addition to logical properties, there are also logical values that can be used in a variety of other CSS properties.
| Logical Value | Physical Value (LTR) |
|---|---|
start |
left |
end |
right |
inline-start |
left |
inline-end |
right |
block-start |
top |
block-end |
bottom |
Snippets Css S Mouse Cursor Gradient Tracking (`content/snippets/css/s/mouse-cursor-gradient-tracking.md`)
title: Mouse cursor gradient tracking language: css tags: [visual,interactivity] cover: tram-car excerpt: Create a hover effect where the gradient follows the mouse cursor, with CSS and a little bit of JavaScript. listed: true dateModified: 2024-08-23
Cursor tracking effects are cool, but they're generally tricky to implement. Luckily, CSS custom properties make implementation much easier, especially when combined with some simple JavaScript for DOM manipulation.
HTML structure
For this example, we'll use a element with a child. The will contain the **text** that will be hovered over. The is necessary, as we will be applying the gradient to the button's ::before pseudo-element. Without the ``, the gradient would be applied on top of the button's text, making it hard to read.
<button class="mouse-cursor-gradient-tracking">
<span>Hover me</span>
</button>The gradient's CSS
As mentioned previously, we'll use the ::before pseudo-element to create the gradient. For this to work, we'll have to use position: absolute on the pseudo-element, which means the `` itself needs to have position: relative.
We'll also need to declare two CSS variables, --x and --y, to track the position of the mouse on the button. We'll make sure to update these with JavaScript a little bit later.
Finally, we'll use a third CSS variable, --size, to modify the gradient's dimensions. This variable, will start at 0, and will be updated to 200px when the button is hovered over.
.mouse-cursor-gradient-tracking {
position: relative;
background: #7983ff;
overflow: hidden;
}
.mouse-cursor-gradient-tracking span {
position: relative;
pointer-events: none;
}
.mouse-cursor-gradient-tracking::before {
--size: 0;
content: '';
position: absolute;
left: var(--x);
top: var(--y);
width: var(--size);
height: var(--size);
background: radial-gradient(circle closest-side, pink, transparent);
transform: translate(-50%, -50%);
transition: width 0.2s ease, height 0.2s ease;
}
.mouse-cursor-gradient-tracking:hover::before {
--size: 200px;
}Tracking the mouse with JavaScript
Tracking the mouse is relatively simple. We'll first use Document.querySelector() to select the button, and then use EventTarget.addEventListener() to register a handler for the 'mousemove' event.
Every time the listener is triggered, we'll use Element.getBoundingClientRect() to get the button's position on the screen, and then use CSSStyleDeclaration.setProperty() to update the values of the --x and --y CSS variables.
When these two values change, the gradient will update its position, making it look like it's following the mouse cursor.
const btn = document.querySelector('.mouse-cursor-gradient-tracking');
btn.addEventListener('mousemove', e => {
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
btn.style.setProperty('--x', x + 'px');
btn.style.setProperty('--y', y + 'px');
});Putting everything together gives us a nice hover effect where the gradient follows the mouse cursor. You can see the final result in the CodePen below:
https://codepen.io/chalarangelo/pen/MWMdMaX
Snippets Css S Navigation List Item Hover And Focus Effect (`content/snippets/css/s/navigation-list-item-hover-and-focus-effect.md`)
title: Navigation list item hover & focus effect language: css tags: [visual] cover: cloudy-rock-formation excerpt: Create a custom hover and focus effect for navigation items, using CSS transformations. listed: true dateModified: 2024-09-19
Creating a custom hover and focus effect for navigation items is a fairly straightforward task, leveraging the power of CSS transformations.
For each element in your navigation list, you can use the ::before pseudo-element to create a hover effect. By default, you can hide it using transform: scale(0). Then, using the :hover and :focus pseudo-class selectors, you can transition the pseudo-element to transform: scale(1) and show its colored background.
Finally, prevent the pseudo-element from covering the anchor element using z-index.
li a {
position: relative;
display: block;
z-index: 0;
}
li a::before {
position: absolute;
content: "";
width: 100%;
height: 100%;
bottom: 0;
left: 0;
background-color: #2683f6;
z-index: -1;
transform: scale(0);
transition: transform 0.5s ease-in-out;
}
li a:hover::before,
li a:focus::before {
transform: scale(1);
}Snippets Css S Nested Border Radius (`content/snippets/css/s/nested-border-radius.md`)
title: Perfect nested border radius in CSS language: css tags: [visual] cover: rocky-beach-waves excerpt: Nesting elements with rounded borders can look very wrong if not done correctly. Here's a quick tip on how to do it right. listed: true dateModified: 2022-04-03
Nesting elements with rounded borders can look very wrong if not done correctly. Luckily, there's a simple math trick to make it look right. All you need to do is calculate the border radius of one of the elements and the distance between them.
The formula
The border radius of the outer element should be equal to the sum of the border radius of the inner element and the distance between the two elements. This can be mathematically expressed as innerRadius + distance = outerRadius or more tersely R1 + D = R2.
Example
Let's take a look at a simple CSS example. Say we want to style two nested boxes with rounded borders. The outer box has a border-radius of 24px and a padding of 8px. Using the previous formula, we can deduce that the inner box should have a border-radius of 16px.
.outer {
border-radius: 24px;
padding: 8px;
}
.inner {
border-radius: 16px;
}CSS Variables
Using CSS variables, we can make this even easier. Depending on the use case, we can adapt the formula to calculate the outer radius from the inner one, or the other way around. Here's an example of how to do this:
.outer {
--outer-radius: 24px;
--padding: 8px;
--inner-radius: calc(var(--outer-radius) - var(--padding));
border-radius: var(--outer-radius);
padding: var(--padding);
}
.inner {
border-radius: var(--inner-radius);
}.outer {
--inner-radius: 16px;
--padding: 8px;
--outer-radius: calc(var(--inner-radius) + var(--padding));
border-radius: var(--outer-radius);
padding: var(--padding);
}
.inner {
border-radius: var(--inner-radius);
}[!NOTE]
This setup may work for simple cases, but if you have multiple
paddingormarginvalues, you may need to tweak it to fit your needs.
Snippets Css S Offscreen (`content/snippets/css/s/offscreen.md`)
title: Hide element offscreen shortTitle: Offscreen language: css tags: [layout,visual] cover: succulent-2 excerpt: Hide an element completely (visually and positionally) in the DOM while still allowing it to be accessible. listed: true dateModified: 2024-09-02
Hiding an element with display: none comes with the drawback of making it inaccessible to screen readers. On the other hand, hiding an element offscreen allows you to keep it accessible while removing it from the visual and positional flow of the page. This is useful for elements that are not meant to be seen but are still required for accessibility purposes (e.g. skip links).
In order to achieve this, you will need to create your own utility class that will hide the element offscreen. This class will remove any border and padding, hide the element's overflow, and make the height and width of the element 1px while negating them using margin. The element will also be positioned absolutely so that it does not take up space in the DOM.
[!TIP]
This technique provides an accessible and layout-friendly alternative to
display: none(not readable by screen readers) andvisibility: hidden(takes up physical space in the DOM).
<a class="button" href="https://google.com">
Learn More <span class="offscreen"> about pants</span>
</a>.offscreen {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}Snippets Css S Perfect Transition Duration (`content/snippets/css/s/perfect-transition-duration.md`)
title: The perfect duration for CSS transitions shortTitle: CSS transition duration language: css tags: [interactivity,visual,animation] 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. listed: true dateModified: 2021-06-12
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.
Snippets Css S Print Stylesheet (`content/snippets/css/s/print-stylesheet.md`)
title: CSS Print Stylesheet language: css tags: [visual] cover: cozy-desk-setup excerpt: A short opinionated print stylesheet to make your websites look great on paper. listed: true dateModified: 2023-06-11
While it's not that often we physically print content from the web, print stylesheets shouldn't be overlooked. They can be used to make sure that the content of your website is presented in a legible and print-friendly manner. Here's a simple, opinionated print stylesheet that you can use as a base for your own:
@media print {
@page {
size: A4;
}
body {
margin: 0;
padding: 0;
}
body, p, h1, h2, h3, h4, h5, h6, li {
font-family: Helvetica, Arial, sans-serif;
font-size: 12pt;
font-weight: normal;
}
h1, h2, h3, h4, h5, h6 {
font-weight: bold;
margin-bottom: 0.5em;
}
h1 {
font-size: 24pt;
}
h2 {
font-size: 18pt;
}
h3 {
font-size: 14pt;
}
a:any-link {
color: #0000FF;
text-decoration: none;
}
a:any-link::after {
content: " [" attr(href) "] ";
}
img {
width: 100%;
}
header, footer, nav, aside, form, iframe, script {
display: none;
}
}@media print- The print media query is used to apply styles when the page is printed.@page:size- Specify the page size.
body:margin- Remove the default margin.padding- Remove the default padding.
body, p, h1, h2, h3, h4, h5, h6, li:font-family- Use print-friendly fonts.font-size- Use a legible font size.font-weight- Reset the font weight.
h1, h2, h3, h4, h5, h6:font-weight- Make headings bold.margin-bottom- Add some space between headings and the content below them.
h1-h3:font-size- Use a larger font size for headings.
a:any-link:color- Use a print-friendly color for links.text-decoration- Remove the underline from links.
a:any-link::after:content- Add the link URL after the link.
img:width- Make images fill the page width.
header, footer, nav, aside, form, iframe, script:display- Remove unnecessary elements from the page.
Snippets Css S Pseudo Classes (`content/snippets/css/s/pseudo-classes.md`)
title: What are CSS pseudo-classes? shortTitle: CSS pseudo-classes language: css tags: [webdev] cover: orange-flower excerpt: Learn how to use CSS pseudo-classes to style an element based on changes to its state. listed: true dateModified: 2021-11-07
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.
Pseudo-classes let you apply styles to elements in relation to the content of the document tree (e.g. :first-child), external factors such as the history of the navigator (e.g. :visited), the status of their content (e.g. :checked) or the position of the mouse (e.g. :hover).
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 for more information.
:hover,:focusand:activeare used to provide feedback for user interaction (e.g. changing a button's color on hover):linkand:visitedare useful for styling links based on navigation history (e.g. changing the color of visited links):first-child,:last-child,:nth-child()and:nth-last-child()are useful when working with collections of elements:not()is used to match everything except the given selector and can be useful in styling hard to select elements:lang()allows you to apply special styles based on the language of the document and is useful for multilingual websites
Snippets Css S Reset All Styles (`content/snippets/css/s/reset-all-styles.md`)
title: How can I reset all styles on an element? shortTitle: Reset all styles language: css tags: [visual] cover: rocky-beach-2 excerpt: Have you ever needed to reset all styles previously applied to a selector to their default values? CSS has the tool just for you! listed: true dateModified: 2024-08-15
Have you ever needed to reset all styles previously applied to a selector to their default values? CSS has the tool just for you!
One of the lesser-known properties in CSS is the all property. This property is used to set properties to their initial or inherited values, or to the values specified in another cascade layer or stylesheet origin.
Using it in conjunction with the initial value, you can reset all styles to their default values. However, this will not affect the direction and unicode-bidi properties, as well as any custom properties you may have defined.
.reset-all-styles {
all: initial;
}After resetting all styles, you can then apply new styles to the element as needed. Remember that this property should come first in the CSS rule to ensure that it takes precedence over any other styles applied to the element.
Snippets Css S Reset (`content/snippets/css/s/reset.md`)
title: CSS Reset language: css tags: [visual] cover: bridge-over-road excerpt: A short, opinionated CSS reset to make your websites look great everywhere. listed: true dateModified: 2022-10-16
Browsers nowadays are much better at presenting HTML in a consistent manner, making CSS resets of the past largely unnecessary. However, default browser styles are not particularly great in most cases, which is why there are tons of CSS resets out there. Drawing inspiration from some of them, here's my opinionated CSS reset, along with an explanation of why I chose to include each rule.
html {
max-width: 70ch;
margin: auto;
padding: 3em 1em;
font-family: system-ui, 'Segoe UI', Roboto, Cantarell,
'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 1.25em;
line-height: 1.75;
}
body {
margin: 0;
}
h1, h2, h3, h4, h5, h6 {
margin: 3em 0 1em;
}
p, ul, ol {
margin-bottom: 2em;
color: #1d1d1d;
}
small {
font-size: 80%;
}
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
pre, code, kbd {
font-family: monospace, monospace;
font-size: 1em;
}html:max-width- Usechunits to set the maximum width in the middle of the optimal readable range (60-80 characters).margin- Center content on the page.padding- Prevent edge-to-edge text on smaller viewports.font-family- Use the system font stack to ensure the best possible font rendering.system-uiis a new generic font family that replaces-apple-systemandBlinkMacSystemFont.font-size- Use a larger font size for better readability and to keep up with recent design trends.line-height- Use a larger line height to increase visual clarity.
body:margin- Remove the default margin in all browsers.
h1-h6:margin- Use larger margins for headers to improve visual hierarchy.
p,ul,ol:margin-bottom- Add spacing between textual elements.color- Soften text color to improve readability.
small:font-size- Correct font size discrepancies between browsers.
sub,sup:font-size- Correct font size discrepancies between browsers.line-height- Prevent elements from affecting line height.position- Position elements relative to parent.vertical-align- Align elements to the baseline.bottom,top- Correctly position elements.
pre,code,kbd:font-family- Use monospace fonts for code elements.font-size- Correct font size discrepancies between browsers.
Snippets Css S Responsive Favicon Dark Mode (`content/snippets/css/s/responsive-favicon-dark-mode.md`)
title: How can I create a custom responsive favicon for dark mode? shortTitle: Responsive dark mode favicon language: css tags: [visual] cover: dark-mode excerpt: Learn how to create a custom responsive favicon that can adapt its color palette for dark mode with this quick guide. listed: true dateModified: 2021-09-28
The rise of dark mode in recent years has made many website favicons feel awkward or even impossible to see in some cases. Provided you have the appropriate assets, it's relatively easy to create a responsive favicon that can adapt to the user's color scheme preferences.
In order to create a responsive favicon, you need an SVG icon with as few colors as possible and two color palettes, one for light mode and one for dark mode. Usual rules about icon clarity and complexity apply, so make sure your icon meets all the necessary criteria to be visually distinguishable in any scenario. In our example, we will be using a monochrome icon from the fantastic Feather icon set.
Leveraging embedded styles in SVG images and the prefers-color-scheme media query, we can create an appropriate <g> element to group all the elements of the icon. Then, using the id of the group, we can apply the color palette for each design. Here's what our final SVG asset looks like:
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<style>
#icon {
stroke: #000;
stroke-width: 2px;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
@media (prefers-color-scheme: dark) {
#icon {
stroke: #fff;
}
}
</style>
<g id="icon">
<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path>
<line x1="3" y1="6" x2="21" y2="6"></line>
<path d="M16 10a4 4 0 0 1-8 0"></path>
</g>
</svg>After creating the SVG asset, you need only include the custom SVG favicon in the page's <head> element and you're ready to go. Be sure to include a PNG fallback, if possible, with a rendered version of the icon in either palette:
<head>
<link rel="icon" type="image/png" href="favicon.png" >
<link rel="icon" type="image/svg" href="favicon.svg" >
</head>Snippets Css S Root Vs Html (`content/snippets/css/s/root-vs-html.md`)
title: What's the difference between :root and html in CSS? shortTitle: :root vs html language: css tags: [layout,selector] cover: tree-roots excerpt: The CSS selectors used to target the root element of an HTML share some similarities, but they also have some differences. listed: true dateModified: 2022-05-22
CSS has two ways to target the root element of an HTML document - the :root pseudo-class and the html selector. While these are very similar to each other, they have a couple of differences you should know.
Selector specificity
The :root selector has a higher specificity than the html selector. This is because :root is a pseudo-class selector, while html is a type selector.
:root {
background-color: red;
}
html {
background-color: blue;
}
/* The HTML document's root element will have a red background-color. */Targeting the root element
CSS can be used to style other types of documents, apart from HTML. This is where the :root element comes in to play, allowing you to style the root element of a document. This can be especially important when styling SVG documents, where the html selector will not work.
Snippets Css S Scroll Progress Bar (`content/snippets/css/s/scroll-progress-bar.md`)
title: Scroll progress bar language: css tags: [animation,visual] cover: coworking-space excerpt: Create a progress bar indicating the scroll percentage of the page, using CSS and JavaScript. listed: true dateModified: 2024-09-22
Horizontal progress-style scroll bars are a fairly new trend in web design. They are often used in tandem with the traditional vertical scroll bar to provide a visual representation of the user's progress through the page. So how does one go about implementing this feature?
As far as CSS is concerned, there's very little work to be done. The progress bar itself is just a simple div element with a fixed position at the top of the page.
The magic happens in the JavaScript, where we use EventTarget.addEventListener() to listen for the scroll event. Then, we use the Element.scrollTop property to determine the scroll percentage of the document and apply it to the width of the progress bar.
https://codepen.io/chalarangelo/pen/gOVrBVM
#scroll-progress {
position: fixed;
top: 0;
width: 0%;
height: 4px;
background: #7983ff;
z-index: 10000;
}const scrollProgress = document.getElementById('scroll-progress');
const height =
document.documentElement.scrollHeight - document.documentElement.clientHeight;
window.addEventListener('scroll', () => {
const scrollTop =
document.body.scrollTop || document.documentElement.scrollTop;
scrollProgress.style.width = `${(scrollTop / height) * 100}%`;
});Snippets Css S Scroll Snap (`content/snippets/css/s/scroll-snap.md`)
title: Horizontal or vertical scroll snap in CSS shortTitle: Horizontal or vertical scroll snap language: css tags: [interactivity] cover: purple-sunset-waves excerpt: Create a horizontally or vertically scrollable container that will snap on elements when scrolling. listed: true dateModified: 2024-08-25
Scroll snapping in CSS has been around for a while and it's a great way to create modern interactive experiences. It consists of a handful of properties that allow you to create a scrollable container that will snap on elements when scrolling, either horizontally or vertically.
Scrollable containers that snap
To create a scrollable container that snaps on elements when scrolling, you can use the scroll-snap-type property to control the snap axis. This property can be set to x for horizontal scrolling or y for vertical scrolling. You can also set it to both to enable both horizontal and vertical scrolling.
[!TIP]
If you're working with the new Logical Properties you can use
blockfor vertical andinlinefor horizontal. In this article, however, we'll focus on the more traditionalxandyvalues.
/* Horizontal scroll snap */
.scroll-snap {
scroll-snap-type: x mandatory;
}
/* Vertical scroll snap */
.scroll-snap {
scroll-snap-type: y mandatory;
}Apart from the snap axis, you can also specify the snap strictness with scroll-snap-type. The mandatory value will snap the container to the nearest snap point, while proximity will snap the container to the nearest snap point if it's close enough (default).
Snap alignment
You can also control the alignment of the snap points using the scroll-snap-align property. This property can be set to start, end, center, or none.
/* Snap to the start of the element */
.scroll-snap > .element {
scroll-snap-align: start;
}
/* Snap to the center of the element */
.scroll-snap > .element {
scroll-snap-align: center;
}
/* Snap to the end of the element */
.scroll-snap > .element {
scroll-snap-align: end;
}Scrolling over the edge
To prevent the default browser behavior when reaching the end of the scrollable container, you can use the overscroll-behavior property. This property can be set to auto, contain, or none.
The contain value is usually preferred as it allows the scroll snap to work as expected when scrolling over the edge of the container.
[!TIP]
Depending on your use case, you can use
overscroll-behavior-xandoverscroll-behavior-yto control the behavior on the horizontal and vertical axes respectively, or evenoverscroll-behavior-inlineandoverscroll-behavior-blockif you're using Logical Properties.
/* Horizontal scroll snap */
.scroll-snap {
scroll-snap-type: x mandatory;
overscroll-behavior-x: contain;
}
/* Vertical scroll snap */
.scroll-snap {
scroll-snap-type: y mandatory;
overscroll-behavior-y: contain;
}Horizontal scroll snap
Putting everything together, we can create a horizontally scrollable container that will snap on elements when scrolling. At its core the HTML and CSS is quite simple. All you need to do is set the scroll-snap-type to x mandatory and the overscroll-behavior-x to contain. You can decide on the value of your scroll-snap-align based on your design.
https://codepen.io/chalarangelo/pen/VwoYQYj
<div class="horizontal-snap">
</div>.horizontal-snap {
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
}
.horizontal-snap > .element {
scroll-snap-align: center; /* or start, end */
}Vertical scroll snap
Similarly, a vertically scrollable container that snaps on elements when scrolling can be created by setting the scroll-snap-type to y mandatory and the overscroll-behavior-y to contain. You can again decide the value of scroll-snap-align based on your design.
https://codepen.io/chalarangelo/pen/mdNyXVm
<div class="vertical-snap">
</div>.vertical-snap {
overflow-y: auto;
overscroll-behavior-y: contain;
scroll-snap-type: y mandatory;
}
.vertical-snap > .element {
scroll-snap-align: center; /* or start, end */
}Snippets Css S Select Any Link (`content/snippets/css/s/select-any-link.md`)
title: Select any link with CSS shortTitle: "CSS :any-link pseudo-class" language: css tags: [visual,interactivity] cover: round-leaves excerpt: You can use a CSS pseudo-class selector to style all links in a page, without worrying if they have been visited or not. listed: true dateModified: 2022-03-06
Styling links with CSS is considered straightforward, with most developers using the :link and :visited pseudo-classes. While this solution is very common, there's a less verbose alternative in the form of the :any-link pseudo-class. This pseudo-class selects all links, regardless of whether they have been visited or not. Thus, it acts as a catch-all for all links on the page.
:any-link {
color: #0444f6;
}One important note is that using :any-link is different to using the [href] attribute selector. The :any-link pseudo-class does not select empty links, whereas the [href] attribute selector does.
Snippets Css S Shake Invalid Input (`content/snippets/css/s/shake-invalid-input.md`)
title: Shake input field when invalid, using CSS shortTitle: Shake on invalid input language: css tags: [animation] cover: perfect-timing excerpt: Inform your users of invalid input by shaking the input field. listed: true dateModified: 2024-08-31
One of the most common feedback animations is shaking an input field when the user enters invalid data. Luckily, HTML and CSS have just the right tools to help you create this effect.
Starting with the HTML, we'll need to use the pattern attribute to define the regular expression which the input's value must match. This will trigger the :invalid pseudo-class when the input is invalid.
Then, using @keyframes, we can define a shake animation that uses the margin-left property to move the element back and forth. Finally, we can use the :invalid pseudo-class to apply the animation to the element, making it shake when the user enters invalid data.
<input type="text" placeholder="Letters only" pattern="[A-Za-z]*" />input:invalid {
animation: shake 0.2s ease-in-out 0s 2;
}
@keyframes shake {
0% {
margin-left: 0rem;
}
25% {
margin-left: 0.5rem;
}
75% {
margin-left: -0.5rem;
}
100% {
margin-left: 0rem;
}
}https://codepen.io/chalarangelo/pen/dyxorLK
Snippets Css S Shapes (`content/snippets/css/s/shapes.md`)
title: Create shapes using CSS shortTitle: Shapes language: css tags: [visual,cheatsheet] cover: oven-paddle excerpt: Use CSS to create various shapes like circles, triangles, and squares. listed: true dateModified: 2024-05-08
CSS can be used to create various shapes like circles, triangles, and squares. While this technique is slowly falling out of favor, it can sometimes be worth using for simple shapes, instead of using SVG or images.
https://codepen.io/chalarangelo/pen/gOJOwwo
Square & Rectangle
A simple CSS square can be created by setting the width and height to the same value. Similarly, a rectangle can be created by setting the width and height to different values.
<div class="square"></div>
<div class="rectangle"></div>.square {
width: 64px;
height: 64px;
background: #5394fd;
}
.rectangle {
width: 128px;
height: 64px;
background: #5394fd;
}Circle & Ellipse
Using the border-radius property, a circle can be created by setting it to 50%. The width and height must be the same to create a circle. Subsequently, an ellipse can be created by providing two values for the border-radius and setting the width and height to different values.
<div class="circle"></div>
<div class="ellipse"></div>.circle {
width: 64px;
height: 64px;
border-radius: 50%;
background: #5394fd;
}
.ellipse {
width: 128px;
height: 64px;
border-radius: 64px / 32px;
background: #5394fd;
}Triangles
A triangle can be created by using three borders. The border-width should be the same for all borders, and the opposite side of where the triangle points towards should have the desired border-color. The adjacent borders should have a border-color of transparent.
<div class="triangle-down"></div>
<div class="triangle-up"></div>
<div class="triangle-left"></div>
<div class="triangle-right"></div>.triangle-down {
width: 0;
height: 0;
border-top: 32px solid #5394fd;
border-left: 32px solid transparent;
border-right: 32px solid transparent;
}
.triangle-up {
width: 0;
height: 0;
border-bottom: 32px solid #5394fd;
border-left: 32px solid transparent;
border-right: 32px solid transparent;
}
.triangle-left {
width: 0;
height: 0;
border-right: 32px solid #5394fd;
border-top: 32px solid transparent;
border-bottom: 32px solid transparent;
}
.triangle-right {
width: 0;
height: 0;
border-left: 32px solid #5394fd;
border-top: 32px solid transparent;
border-bottom: 32px solid transparent;
}Snippets Css S Sibling Fade (`content/snippets/css/s/sibling-fade.md`)
title: Fade out siblings on hover shortTitle: Sibling fade language: css tags: [interactivity] cover: messy-papers excerpt: Create a cool effect that fades out the siblings of a hovered item with a few lines of CSS. listed: true dateModified: 2024-08-28
A classic hove effect is to fade out the siblings of an item when it is hovered. This can be achieved with a few lines of CSS by using the :hover and :not() pseudo-class selectors.
Simply put, when the mouse is over an element, all its siblings will have their opacity set to 0.5, while the hovered element will remain at 1. Adding a transition to the opacity property will create a smooth animation effect.
Using the :hover selector, we can target the parent of the hovered element and change the opacity of all its children except the one being hovered. This technique works because the :hover selector is applied to the parent element, as well as the child element.
Alternatively, we can use the new :has() pseudo-class in combination with :hover to achieve the same effect. The :has() pseudo-class allows us to select an element based on whether it has a descendant that matches a specific selector.
.element {
transition: opacity 0.3s;
}
/* Using the :hover and :not() pseudo-class selectors */
.container:hover .element:not(:hover) {
opacity: 0.5;
}
/* Or using the :has() pseudo-class */
.container:has(.element:hover) .element:not(:hover) {
opacity: 0.5;
}https://codepen.io/chalarangelo/pen/wvVamWR
Snippets Css S Speech Bubble (`content/snippets/css/s/speech-bubble.md`)
title: Speech bubbles with CSS shortTitle: Speech bubbles language: css tags: [visual] cover: greek-coffee excerpt: Create a content container with a triangle at the top, much like a speech bubble. listed: true dateModified: 2024-09-05
Speech bubbles have existed in comics for decades, and they are a great way to visually represent dialogue. You can create a similar effect using CSS, by adding a triangle to the top of a content container.
For this task, you'll leverage the ::before and ::after pseudo-elements to create two triangles. The colors of the two triangles should be the same as the container's border-color and the container's background-color respectively.
The border-width of the one triangle (::before) should be 1px wider than the other one (::after), in order to act as the border. The smaller triangle (::after) should be 1px to the right of the larger triangle (::before) to allow for its left border to be shown.
And with that, you have a content container with a triangle at the top, much like a speech bubble. You can tweak this effect to also place the triangle at the bottom, left, or right of the container, or even create a different shape altogether.
.container {
position: relative;
background: #ffffff;
border: 1px solid #dddddd;
}
.container::before,
.container::after {
content: '';
position: absolute;
bottom: 100%;
left: 19px;
border: 11px solid transparent;
border-bottom-color: #dddddd;
}
.container::after {
left: 20px;
border: 10px solid transparent;
border-bottom-color: #ffffff;
}https://codepen.io/chalarangelo/pen/dyxYOap
Snippets Css S Squiggle Link Hover Effect (`content/snippets/css/s/squiggle-link-hover-effect.md`)
title: Squiggle link hover effect language: css tags: [animation,visual] cover: dreamy-flowers excerpt: Create the most whimsical link hover effect with a squiggle animation. listed: true dateModified: 2024-09-15
One of the tiniest details I see people get creative with is the hover effect on links. One of the most whimsical effects I've seen is the squiggle effect. It's a simple yet effective way to make your links stand out and give them a playful touch, making the experience more memorable.
https://codepen.io/chalarangelo/pen/eYqZRdx
The SVG image
The technique once again hinges on the use of SVG images in CSS. This time around we're going to use them as the background-image of the link. The SVG will contain a squiggly path that we'll animate to create the squiggle effect. The animation will be a simple translation of the path along the x-axis, creating the illusion of a squiggle.
As we'll have to data encode our SVG, here's what it looks like in plain text:
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 4'>
<style type='text/css'>
.squiggle {
animation: shift .3s linear infinite;
}
@keyframes shift {
from {
transform: translateX(0);
} to {
transform: translateX(-15px);
}
}
</style>
<path
fill='none'
stroke='#0087ca'
stroke-width='2'
class='squiggle'
d='M0,3.5 c 5,0,5,-3,10,-3 s 5,3,10,3 c 5,0,5,-3,10,-3 s 5,3,10,3'
/>
</svg>Putting it to use
As mentioned already, we'll use background-image with the url() function to animate the :hover state of the link. The SVG will be data encoded in the URL, and we'll set the background-size to auto 4px to ensure the squiggle path is visible.
However, we'll have to create a repeating background for the link to make the squiggle effect work. We'll use a linear-gradient() with a background-size of 2px 2px to create a repeating pattern. The background-position will be set to 0 100% to ensure the gradient starts at the bottom of the link.
This way, when not hovered, the link will technically use the gradient as its background-image. When hovered, the SVG will replace the gradient, creating the squiggle effect.
<p>The <a class="squiggle" href="#">magnificent octopus</a> swam along gracefully.</p>a.squiggle {
background-image: linear-gradient(to bottom, #0087ca 0%, #0087ca 100%);
background-position: 0 100%;
background-repeat: repeat-x;
background-size: 2px 2px;
color: inherit;
text-decoration: none;
}
a.squiggle:hover {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 4'%3E%3Cstyle type='text/css'%3E.squiggle{animation:shift .3s linear infinite;}@keyframes shift {from {transform:translateX(0);}to {transform:translateX(-15px);}}%3C/style%3E%3Cpath fill='none' stroke='%230087ca' stroke-width='2' class='squiggle' d='M0,3.5 c 5,0,5,-3,10,-3 s 5,3,10,3 c 5,0,5,-3,10,-3 s 5,3,10,3'/%3E%3C/svg%3E");
background-size: auto 4px;
}Snippets Css S Staggered Animation (`content/snippets/css/s/staggered-animation.md`)
title: Staggered list animation shortTitle: Staggered animation language: css tags: [animation] cover: aerial-view-port excerpt: Staggered animations can be used to create a more dynamic user experience. Get creative with your lists! listed: true dateModified: 2024-09-01
The term staggered animation refers to an animation effect where elements are animated one after the other, with a slight delay between each element. This can be used to create a more dynamic user experience, especially when dealing with lists.
For this effect to work, we can leverage CSS variables. Each child element will define a value for a custom property (e.g. --i), which will be used to calculate the delay for the animation. By setting the transition-delay property to a multiple of this custom property, we can create a staggered effect.
The rest of the process is pretty standard. We'll use a transition to animate the opacity and transform properties of the list elements. When the animation is toggled (e.g. by opening a menu), the list elements will transition from being transparent (opacity: 0) and off-screen (transform: translateX(100%)) to being visible (opacity: 1) and in their final position (transform: translateX(0)).
[!NOTE]
For this example, we'll use a checkbox to toggle the animation. You can use JavaScript or any other method to toggle the animation, based on your needs.
https://codepen.io/chalarangelo/pen/ExqjMBw
<div class="container">
<input type="checkbox" name="menu" id="menu" class="menu-toggler">
<label for="menu" class="menu-toggler-label">Menu</label>
<ul class="stagger-menu">
<li style="--i: 0">Home</li>
<li style="--i: 1">Pricing</li>
<li style="--i: 2">Account</li>
<li style="--i: 3">Support</li>
<li style="--i: 4">About</li>
</ul>
</div>.container {
overflow-x: hidden;
width: 100%;
}
.menu-toggler {
display: none;
}
.stagger-menu li {
opacity: 0;
transform: translateX(100%);
transition-property: opacity, transform;
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0.750, -0.015, 0.565, 1.055);
}
.menu-toggler:checked ~ .stagger-menu li {
opacity: 1;
transform: translateX(0);
transition-delay: calc(0.055s * var(--i));
}Snippets Css S Style Default Links (`content/snippets/css/s/style-default-links.md`)
title: Style links without a class shortTitle: Style default links language: css tags: [visual,interactivity] cover: citrus-drink excerpt: A short summary of your story up to 180 characters long. listed: true dateModified: 2022-11-23
When styling injected or generated HTML content, you might not have access to the classes or IDs of the elements you want to style. This can become especially annoying when dealing with link elements. Luckily, you can use the :not() selector with an appropriate attribute selector to check for the absence of a class and style links accordingly.
a[href]:not([class]) {
color: #0077ff;
text-decoration: underline;
}As a bonus tip, you can use the previous tip about selecting any link to further enhance this solution.
--- METRICS ---
- Files Extracted: 101
- Estimated Token Budget: ~83456 tokens
- Canonical Reference: https://codewiki.google/github.com/Chalarangelo/30-seconds-of-code