Based on Remix, getting data from GitHub, deploying to Netlify.
blog's Introduction
blog's People
Forkers
neokoenigblog's Issues
New blog-like thing
Hello world!
I will be using this little space I've created here mainly as a way to write down notes on web development to future me. Rather than searching through old projects for code, I'll start posting here so I always have it handy.
The site itself is very much a work in progress. At the moment I'm using the GitHub issues API as the backend for the blog. In other words, I post an issue there, it ends up here. The frontend is plain HTML, CSS and JavaScript. No frameworks, no bundling. Nice and simple.
If you're interested in doing something similar, you can have a look at the repository, and if you have any thoughts, get in touch with me through Twitter.
Thanks for reading.
Vanilla JavaScript routing
Ignoring TypeScript files when deploying to Azure
Writing this as a note to myself mostly (as most things on this blog).
I ran into an issue where my .NET Core app could no longer be deployed to Azure. Turns out I had recently introduced some TypeScript files that were now failing in the Kudu / MSBuild step (due to differences in the version of TypeScript used on my machine versus the one used on Azure I believe). Since those TypeScript files are related to Vue and not the .NET Core app, I simply wanted the build step to ignore these files.
To do that, I added the following to my .csproj
file.
<PropertyGroup>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup>
Counting words per line with JavaScript
Emojis in .NET Core / MySQL
To support emojis when using MySQL in .NET Core (version 2.2 in my case) you need to do a few things.
First of all, add CharSet=utf8mb4;
to your database connection string. In other words, your code in Startup.cs
should look something like this.
string db = @"Server=domain.com;Database=db;Uid=user;Pwd=pass;CharSet=utf8mb4;";
services.AddDbContext<AppDbContext>(options => options.UseMySQL(db));
You also need to make a couple of database changes. The charset needs to be set to utf8mb4
both for the column you are storing the emojis in and as the default for the entire database.
Dump object to console xunit
ObjectDumper.net
console verbosity
iOutputHelper
Ideal number of characters per line
To make your website text as readable as possible it's important to use the right number of characters to display on each line.
If your lines are really long, the reader will have a hard time finding the next line to read (because it's too far away from the beginning). If they're too short however, the reader will lose their rhythm because their eyes will have to track back too often.
But is there a magic number to aim for?
Most experts seem to indicate that having anywhere from 45 to 120 characters per line is fine. In other words, it won't cause the reader any noticeable problems if you keep it within that range. 55 to 65 characters is the most optimal for print, but there are studies that seem to suggest you can get away with a slightly higher number of characters on the web. The official W3C recommendation is to keep line length under 80 characters.
Given all that, I tend to aim for around 70 characters per line.
I do make an exception when targeting phones though. Rather than making the font really small to try and accommodate the 70 characters per line rule, I'm ok with ending up at around 50 characters per line instead.
Sorting an array of objects
Also include how to sort by multiple properties (like a boolean first and then name within that for example).
Highlight table row when hovering over specific table cell
Highlighting a table row when hovering over it is easy, you just use the :hover
CSS pseudo-class. But what if you only want to highlight the table row when you hover over a specific table cell? It's a little more difficult because you can't achieve it using just CSS, you have to throw in some JavaScript as well.
<style>
.highlight {
background: #e2e8f0;
}
</style>
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data 1</td>
<td class="hoverable">Data 2</td>
<td>Data 3</td>
</tr>
</tbody>
</table>
<script>
function highlightRow(event, fn) {
event.target.closest("tr").classList[fn]("highlight");
}
document.querySelectorAll(".hoverable").forEach((elm) => {
elm.addEventListener("mouseenter", (e) => highlightRow(e, "add"));
elm.addEventListener("mouseleave", (e) => highlightRow(e, "remove"));
});
</script>
Best choice for setting line height
Comment notes
Always above code, explain what block of code does
Blank line after block of code
On the right only if it fits and is a list of things
Use pseudo-comments, leave the relevant ones in place after coding
Explain in variable / function naming when possible / pragmatic, comment when not
Table scroll tabindex
mutationobserver
Do not use fat arrow in Vue data function
Dump object
ObjectDumper.net
console verbosity
iOutputHelper
Nullish coalescing operator in Vue templates
The nullish coalescing operator can be really useful when you want to display, for example, "N/A" when a value is null or undefined, but not when it's 0.
<template>
<p>{{ val ?? 'N/A' }}</p>
</template>
<script>
export default {
data() {
return {
val: 0
}
}
}
</script>
Note that you need Vue 3 for this to work in templates.
Fuzzy search in JavaScript
Get the domain from a URL
Use the trick to create an a element and get hostname, then regex to get the last two parts of the domain name.
Git ignore a folder with exceptions
Usually when working with .gitignore
files it's pretty straight forward, you ignore some folders and / or files and you're done. But what if you want to ignore everything inside a folder except for specific folders and files inside it?
Here's how you can do that.
folder/*
!folder/tracked-folder
!folder/tracked-file.js
As you can see the !
prefix is used here. It negates the pattern so that any matching file excluded by a previous pattern will become included again.
Get background color of elements
How to reset Entity Framework Core migrations
Delete all records in the __EFMigrationsHistory
table.
Delete all files in the Migrations
folder.
Run dotnet ef migrations add Initial
.
So far we haven't made any database changes at all, just created the migration file itself. The problem now is that you can't run that migration because the tables already exist in the database.
Comment out everything in the Up
method.
Run dotnet ef database update
.
Now you have applied the migration but nothing in the database changed since the code was commented out.
WCAG color contrast guide
Fetch wrapper
raw query / keyless / interpolate / parameterize
TypeScript and functional React components
Here's an example of how I typically add TypeScript types to a functional React component.
import { ReactNode } from "react";
type CmpProps = {
children: ReactNode;
classes: string;
};
export function Cmp({ children, classes }: CmpProps): JSX.Element {
return <div className={classes}>{children}</div>;
}
I prefer this method over using the built-in React.FC
type because now I can specify that the children property is required, rather than having it optional as it is in React.FC
. This way, if you forget to include the children for the component, TypeScript will scream at you (or if you include children when it should not be included).
Also, note the JSX.Element
return type for the function. That can be useful to have in there to catch some silly mistakes made with formatting for example. If you didn't have it specified you would still likely get a TypeScript error somewhere but it would be from where you consume the component instead, which is a little less obvious.
Checking for nulls in C#
Checking for nulls is one of those things that leads to a lot of boilerplate code in C#. As such, I want it to take up as little space as possible so it doesn't distract me from the "real" code.
Here's my current favorite one-liner way of doing it.
_ = myArg ?? throw new ArgumentNullException(nameof(myArg));
This makes use of discards (_
) and the null-coalescing operator (??
).
Discards are placeholder variables that are intentionally left unused.
The null-coalescing operator returns the value on the left side if it isn't null, otherwise it returns the result of the operand on the right side.
In other words, all we're doing here is placing the value of myArg
in an unused variable if it's not null, and when it is null, we throw an error instead.
You could argue that this code is a little weird and not quite as readable as using a full if statement instead, but I think that's a trade-off worth doing in this case.
By the way, there's an even better way of checking for nulls on the horizon. A feature called "Simplified Null Argument Checking" may be coming in a future release of C# (it was removed from C# 9 at a late stage). If added, all you have to do is put !
at the end of a parameter name and it will run the same code as above under the hood.
Free up space on Azure
After an automatic deployment from GitHub, the NuGet packages remain in the .nuget folder even though they're not needed anymore. This can become a problem if you have several apps running, each having their own .nuget folder. You'll pretty quickly exceed the 1GB available space (shared D1 plan). To remove them, click on "Advanced Tools" in the menu on the left for your Azure app service. Then go to Debug Console, CMD and you should see a file explorer where you can delete the .nuget folder.
Better way is to build with a no cache setting?
Change font size based on viewport width without using media queries
Example:
font-size: calc(16px + 0.5vw)
Mark up dates in HTML
Creating a dynamic component in Vue
Update BIT in MySQL
How to do it in MySQL Workbench: b'1' for example.
Sticky footers using Tailwind
Creating a footer that sticks to the bottom of the page is fairly easy when using Tailwind (and to be fair, it's not too hard with plain old CSS either).
You can use something like the code outlined below.
<body class="flex flex-col min-h-screen">
<header>
Header content...
</header>
<main class="flex-grow pb-12">
Main content...
</main>
<footer>
Footer content...
</footer>
</body>
That will set the body to always be at least the height of the viewport and the main
element to fill the available space height wise (using Flexbox).
The bottom padding on the main
element is optional, but it's probably something you want to have in order to create some space in the layout before the footer comes.
If you want the footer to actually stick to the bottom of the screen as well (i.e. always be visible regardless of how tall the main content area is), then add sticky
and bottom-0
to the footer
element.
Run dist version of forked npm repo
New branch named local, use Husky, precommit hook, do not ignore dist folder etc, change package.json to use your repo and local branch.
Vue generic modal
Tailwind
Lock focus
Custom component wrapper with Vue 3 syntax
Sanitize HTML using JavaScript
If you're writing JavaScript and need to sanitize a string, here's an easy way to do it.
const html = "<p>Test</p>";
const elm = document.createElement("p");
elm.textContent = html;
const sanitized = elm.innerHTML;
The reason this works is that when you set the potentially dangerous string to the element's textContent
property, it will be escaped. Then you just read it back using innerHTML
and you have a safe string that you can use instead.
Using system fonts in CSS
Better focus outline on rounded buttons
Here's a mistake I remember having made in the past. I would add some rounded corners on a button and then discover that the focus outline looked a little ugly (it would be rectangular and not follow the border radius). Then I would add outline: none
to remove it, thinking I was done. Wrong!
I've now created a problem for keyboard users. If they tab to a button, they can't see that it's in focus because there are no styles to indicate that. In other words, the user interface is not as accessible as it could be.
Here's a better solution (open example page).
button {
border: solid #d3d3d3 1px;
border-radius: 10px;
padding: 10px 20px;
}
button:focus {
box-shadow: 0 0 0 1px #000;
outline: transparent solid 1px;
}
This effectively removes the outline by making it transparent and adds a box shadow to act as the outline instead. The reason for using box-shadow
is that it follows the border radius so it's no longer rectangular.
Note that we make the outline transparent instead of just removing it. This is because this way it still works in high contrast mode in Windows.
Also, if you don't like the boring 1 pixel black outline you can go for something like this instead (open example page).
button:focus {
box-shadow: 0 0 0 3px rgba(164, 202, 254, 0.45);
}
Using a custom arrow in a select drop-down
Let me start by saying that you probably shouldn't do this. Stick to letting the browser render its default look for form elements as much as possible. They will be more familiar to your users that way. That said, let's say you've been told to customize these arrows a little bit, here's how you can do it.
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding-right: 24px;
background-image: url("data:image/svg+xml;utf8,<svg fill='%23808285' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
background-repeat: no-repeat;
background-position-x: right;
background-position-y: 50%;
}
A few things to note...
The appearance
rules will remove all browser specific styling for the select
element, not just on the arrow itself. In other words, you might need to add some more CSS to get it back to looking as you want it to.
Do not use this method if you need to support Internet Explorer 11, it won't work.
You need to encode the # character that's used when setting the color using the fill
attribute on the SVG element. For example, something like <svg fill="#ffffff"></svg>
would become <svg fill="%23ffffff"></svg>
instead.
Clickable table rows
Turns out that making a table row clickable while keeping it accessible, and allowing there to be clickable elements inside it, is a little tricky.
Here's the code I ended up with.
<style>
tbody tr {
cursor: pointer;
}
tbody tr:hover {
background: lightgray;
}
</style>
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<tr role="button" tabindex="0" aria-label="Show row details">
<td>Data 1</td>
<td><button class="skip">Button</button></td>
<td><a href="#" class="skip"><strong>Link</strong></a></td>
</tr>
</tbody>
</table>
<script>
function handleEvent(e) {
const elements = e.composedPath();
for (const elm of elements) {
if (elm.tagName === "TR") {
break;
} else if (elm.classList.contains("skip")) {
return;
}
}
const space = 32;
const enter = 13;
if (e.type === "click" || e.keyCode === space || e.keyCode === enter) {
e.preventDefault();
console.log("Do something for table row event.");
}
}
document.querySelectorAll("tbody tr").forEach((elm) => {
elm.addEventListener("click", handleEvent);
elm.addEventListener("keydown", handleEvent);
});
</script>
The code is accessible because you can get to the table rows using your keyboard and select them by pressing Space or Enter.
It's possible to add links, buttons etc and have their events handled normally by adding a "skip" class to them (or on a parent element).
Common Git commands
Here's a collection of some of the Git commands and workflows I use on a regular basis.
Configure Git with your credentials. This is set globally so you only have to do this once. Remove the --global
flag if you need to override this information for a specific repository.
git config --global user.name "Your Name"
git config --global user.email [email protected]
Initialize a repository (in the folder you're currently in).
git init
Show current status (changed / staged files).
git status
Stage a specific file
git add file.js
Unstage a specific file
git rm --cached file.js
Stage all files
git add .
Commiting
git commit -m "Fix bug"
git log
git log --oneline
Reverting changes
git checkout abc123a (read only view of a specific commit)
git revert abc123a (add new commit to revert a specific commit, not great if another commit relies on the reverted code)
git reset abc123a (remove all commits after that commit but leave files unstaged)
git reset abc123a --hard (remove all commits after that commit, danger since code is now gone)
shift, colon and wq
Branches
git branch -a (show all branches)
git branch feature-x (create new branch)
git checkout feature-x (switch to branch)
git branch -d feature-x (delete branch if it's been merged)
git branch -D feature-x (delete branch regardless of merge status, dangerous since code can be lost)
Merging (be on the branch you want to merge into)
git merge feature-x
If conflict, edit files manually and stage file, make a commit.
get from github / gitlab etc (one folder up)
git clone https://...
git push origin master
existing local git repo:
git remote add origin https://...
git push origin master
git pull origin master
= fetch code so local remote is up to date, then merges that code into current branch
git push origin feature-x
git push -u origin feature-x (sets upstream?)
git pull vs git fetch?!
git fetch = updates local remote branch
git pull = merges
Remove input autocomplete colors
#app input:-webkit-autofill:disabled,
#app input:focus:-webkit-autofill:disabled {
box-shadow: 0 0 0 1em #e5e7eb inset;
-webkit-text-fill-color: #4b5563;
}
Query to get customers and the items from their last purchase
Let's say you want to get a list of all customers in your database and also include all items from their last purchase. How would you do it?
Here's one way to do it using a sub query.
SELECT customers.firstname, customers.lastname, purchases.createdat, items.name
FROM customers
INNER JOIN
(
SELECT customerid, MAX(createdat) AS createdat
FROM purchases
GROUP BY customerid
) AS lastpurchase ON customers.id = lastpurchase.customerid
INNER JOIN purchases ON lastpurchase.customerid = purchases.customerid AND lastpurchase.createdat = purchases.createdat
INNER JOIN items ON purchases.id = items.purchaseid
I like this solution because the query is easy to read and understand. However, there are other ways to accomplish the same thing. One improvement was suggested by Jacob Bogers on Twitter. You can speed up the query by replacing the sub query portion of the query above with the following SQL instead.
SELECT customerid, createdat
FROM purchases a
WHERE NOT EXISTS
(
SELECT 1
FROM purchases b
WHERE a.customerid = b.customerid AND a.createdat < b.createdat
)
Some other considerations.
- If you want to include customers without purchases, just replace all
INNER JOIN
statements withLEFT OUTER JOIN
statements instead. - If you use auto-incrementing primary keys in the purchases table you can call
MAX
on that column instead of thecreatedat
column.
Usage of autocomplete values for accessibility
Don't forget to use key when reusing elements
https://vuejs.org/v2/guide/conditional.html#Controlling-Reusable-Elements-with-key
Or if not using that, how to make sure all data gets updated within the component?
Calculate age in JavaScript
Syncing a forked repository with Git
Something I run into every now and then is that I'll fork a project repository, fix a bug and submit a pull request. Some time goes by and I want to contribute something else to the project. My fork is now most likely out of date and I need to sync it with the project repository again.
In technical terms, what you need to do is configure a "remote" that points to the "upstream" repository. You can check the currently configured remote repository for your fork using this command:
git remote -v
Look for the "upstream" entries. If nothing is listed as "upstream", go ahead and configure a new remote upstream repository like this:
git remote add upstream https://github.com/OWNER/REPOSITORY.git
Once you've configured your remote you can do the following (from the master branch in your fork) every time you need to sync:
git fetch upstream
(fetches code from the remote repository).
git merge upstream/master
(merges the changes from upstream/master into your local master branch).
Done!
Conditional event handler in Vue
Bit of a quick entry to write down how to conditionally add an event handler in Vue that passes a custom argument to its handler function (in addition to the event itself).
Here's the template code.
<input v-on="active ? { input: event => handle('Test', event) } : {}" />
Here's the relevant JavaScript.
data: {
active: true,
},
methods: {
handle(str, event) {
console.log(str);
console.log(event.target.value);
}
}
CSS highlight effect to indicate important text
Here's a nice little yellow marker / highlight / glow effect that you can use to make text stand out on the page. It's achieved by layering text shadows on top of each other.
:root {
--highlight: rgba(255, 193, 7, 0.5);
}
strong {
font-weight: 500;
text-shadow:
-0.5em 0 1em var(--highlight),
0.5em 0 1em var(--highlight),
1em 0 1em var(--highlight),
-1em 0 1em var(--highlight);
}
You can tweak the shadows to make the effect more similar to a yellow marker by decreasing the blur radius (the third value in all the shadows) and increasing the opacity of the color.
As a small side note, I've chosen to use the strong
element for how I use this effect on my blog. I feel this is the most semantically correct HTML element to use since my intention is to indicate that a portion of the text is more important than the rest of it.
I've noticed that it's quite common to use the mark
element for this sort of thing as well. However, I'm not sure that it's the most semantically correct HTML element to use in this case. The HTML specification says that "when used in the main prose of a document, it indicates a part of the document that has been highlighted due to its likely relevance to the user's current activity". There's no relevance to what the user is doing in this case, which is why I prefer the strong
element instead.
If you are marking something up that has to do with the user's activity, it would be totally fine to use the mark
element though. One common use case of that is when the user is doing a search and you want to highlight the text the user searched for within the result text.
Fade in Google font on load
It's been bothering me for a while now that when you load this blog you see a brief display of a fallback font before it gets replaced with the custom one (the Inter font family in my case). So, I decided to do something about it. Here's what I did.
First of all, I now tell the browser to not show a fallback font while downloading the custom one by setting font-display
to block
(technically this will render an invisible fallback font).
Since Google is supplying the CSS for the font we need to tell Google to use this setting though. This is done by passing the URL parameter display=block
instead of the usual display=swap
.
<link href="https://fonts.googleapis.com/css2?family=Inter&display=block" rel="stylesheet" />
Secondly, we can make the user experience a little nicer by fading in the custom font once it's been loaded by the browser. This is done by using the CSS Font Loading API.
The gist of it is that I set the main
element to have an opacity of 0 to begin with. Then I use the Font Loading API to detect when the font has been loaded and start a CSS animation to set the opacity to 1. You can get the full details of this by viewing the source code of the example page.
Some notes to keep in mind if you decide to do something like this yourself:
- By not showing a fallback font you're making the site less accessible. For people on slow connections it will take more time before they can start reading your website.
- If you want to support older browsers you need to use a library that can detect when a web font has been loaded (such as Font Face Observer for example).
- I chose to fade in the entire
main
element but in some cases it may make more sense for you to fade in thebody
element, or just the text itself.
Debouncing the window resize event
Just a quick tip if you want to run some code on window resize but want to avoid freezing / slowing down the user's browser. The code below will run doSomething
after the user is "done" resizing the window (in this case 500ms after the last resize event).
window.addEventListener('resize', () => {
window.clearTimeout(window.resizeTimeout)
window.resizeTimeout = window.setTimeout(() => {
doSomething(this.someVariable)
}, 500)
})
Lining up menu items horizontally
Here's the semantic HTML that's typically used to create a menu:
<nav>
<ul>
<li><a href="/">Menu Item 1</a></li>
<li><a href="/">Menu Item 2</a></li>
<li><a href="/">Menu Item 3</a></li>
</ul>
</nav>
There are (at least) two ways to get these menu items to line up next to each other on the page (horizontally instead of vertically that is). Either you can use display: inline-block
on the li
elements, or you can use display: flex
on the ul
container element.
You'll often see the inline block approach in older code bases and the more modern flexbox approach in more recent code.
One issue with the inline block approach is that you'll have to deal with the spaces that show up between the elements, at least if you're trying to achieve a pixel perfect design. It can be solved by setting font-size: 0
on the container element (i.e. the ul
) and then resetting the font size on the individual list elements.
You don't have this problem with the flexbox approach, but you do have to get rid of the browser's default list styling by setting list-style: none
on the ul
element.
You can check out the code in more detail on the example pages here:
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.