Skip to content

Commit

Permalink
fix: errata
Browse files Browse the repository at this point in the history
  • Loading branch information
learosema committed Jan 17, 2024
1 parent 6e15e74 commit 081a6bc
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 15 deletions.
4 changes: 2 additions & 2 deletions src/assets/css/config/_themes.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
--footer-fg: var(--rose-1);

--code-fg: var(--green-1);
--code-bg: var(--grey-6);
--code-bg: var(--gray-5);

--card-bg: var(--gray-5);
--card-fg: var(--gray-1);
Expand Down Expand Up @@ -69,7 +69,7 @@
--footer-bg: var(--rose-5);
--footer-fg: var(--rose-1);

--code-fg: var(--green-4);
--code-fg: var(--green-5);
--code-bg: var(--gray-1);

--card-bg: var(--gray-1);
Expand Down
1 change: 1 addition & 0 deletions src/assets/css/config/_typography.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ a:hover {
}

code:not([class*=language-]) {
font-size: smaller;
color: var(--code-fg, #cfc);
background: var(--code-bg, #000);
padding: 0 .25em;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: Custom JSX in TypeScript
description: JSX can be used with the Vanilla DOM API and TypeScript's built-in JSX transform feature
description: JSX can be used without React, here's how it can be used with the DOM API and TypeScript's built-in JSX transform feature
tags:
- typescript
- jsx
date: 2024-01-17
---
I'm in a love-hate relationship with React. In my day-to-day work, I work a lot with React but elsewise I prefer working with more lightweight stacks, keeping the client-side JavaScript load in the browser as lean and tiny as possible.
I'm in a love-hate relationship with React. In my day-to-day work, I work a lot with React. In my personal projects, I prefer working with more lightweight stacks, keeping the client-side JavaScript load in the browser as lean and tiny as possible.

Still, there are certain use-cases where I need to generate tags on the client-side from time to time, usually everytime where I need JavaScript anyway for a certain functionality.
Still, there are certain use-cases where I need to generate markup on the client-side from time to time, usually everytime where I need JavaScript anyway for a certain functionality.

## Code Example

Expand All @@ -35,7 +35,7 @@ export class GameMenu extends HTMLElement {
renderTree(this,
<>
<button class="burger" aria-controls="gameMenu" aria-label="open menu">
<svg viewBox="0 0 16 16" fill="currentColor">
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
<rect x="1" y="1" width="14" height="3" />
<rect x="1" y="6" width="14" height="3" />
<rect x="1" y="11" width="14" height="3" />
Expand Down Expand Up @@ -71,11 +71,11 @@ export class GameMenu extends HTMLElement {

The methods `connectedCallback` and `disconnectedCallback` are quite similar to the React lifecycle methods `componentDidMount` and `componentWillUnmount`.

One major difference to React is the fact I'm using `class` attributes rather than `className`. I also found that confusing. React chose to do so as `class` and `for` are reserved JavaScript keywords since ES6 classes arrived, but apparently it works also fine with `class` and I prefer to stick to HTML as close as possible.
One major difference to React is the fact I'm using `class` attributes rather than `className`. React chose to do so as `class` and `for` are reserved JavaScript keywords. Apparently, it works fine with `class` attributes so I prefer to stick to HTML as close as possible.

You could add certain transformations in your own JSX implementation to make both `class` and `className` work, but I decided to not do that.

I also kept the event handling separate and just went with using `addEventListener`.
I also kept the event handling separate and just went with using `addEventListener`. With some additional effort, we could also add support for adding event handlers in a declarative way, like React does.

## Setting up JSX for TypeScript

Expand All @@ -91,11 +91,11 @@ You can enable JSX for TypeScript by editing the `tsconfig.json` by setting a co

There are multiple different types of JSX implementations. I'm using the legacy JSX factory implementation for now which was used in React up to version 16.

React 17 introduced a new kind of JSX factory which I don't use yet. I will cover the differences of the old and new JSX in a follow-up article.
React version 17 introduced a new kind of JSX factory which I don't use yet. I will cover the differences of the old and new JSX in a follow-up article.

## Providing an implementation for the JSX factory

In order to make the factory work, provide implementations for the `h()` function and also for `fragment`.
In order to make the JSX factory work, we need to provide implementations for the `h()` function and also for `fragment`.

Im my case, `h()` returns an object using a recursive `DOMTree` interface. It describes the structure of the generated JSX element.

Expand All @@ -114,14 +114,19 @@ const svgNS = 'http://www.w3.org/2000/svg';

export const fragment = 'fragment';

export function h(tagName: string, attribs: Record<string, string>, ...children: DOMTree[]): DOMTree {
export function h(
tagName: string,
attribs: Record<string, string>,
...children: DOMTree[]): DOMTree {
return {
tagName, attribs, children
};
}
```

In a former version of my `h()` implementation, I created actual dom nodes directly via `document.createElement` instead of returning a data structure, but in order to make inline SVG code work, it is important to switch to the XML namespace pointing to `http://www.w3.org/2000/svg` as soon as there is an `<svg>` tag, so my nodes need to know about their parent elements.
In a former version of my `h()` implementation, I created actual dom nodes directly via `document.createElement` instead of returning a data structure.

But in order to make inline SVG code work, it is important to switch to the XML namespace pointing to `http://www.w3.org/2000/svg` as soon as there is an `<svg>` tag, so my nodes need to know about their parent elements.

As a basic solution, I created a recursive `renderTree` function that takes care of that, using the `element.namespaceURI` property to retrieve the current XML namespace.

Expand Down Expand Up @@ -161,9 +166,9 @@ export function renderTree(node: Element, tree: DOMTree) {

## Providing JSX type definitions

Finally, you need to declare a JSX namespace in a `.d.ts`.
Finally, you need to declare a JSX namespace in a type declaration file, suffixed `.d.ts`.

Be careful with the naming (when the factory is in `jsx.ts`, don't name the type definition file `jsx.d.ts` as there may be conflicts).
Be careful with the naming of the files. When the factory is in `jsx.ts`, don't name the type definition file `jsx.d.ts` as there may be conflicts.

```ts
declare interface DOMTree {
Expand Down Expand Up @@ -202,4 +207,4 @@ const { h, fragment } = require('./src/utils/jsx.ts');
const { MyComponent } = require('./src/components/my-component.tsx');
```

For rendering the component server-side, you will need a DOM implementation. [JSDOM](https://github.com/jsdom/jsdom) or [LinkeDOM](https://github.com/WebReflection/linkedom) will do. I feel I should also write an in-depth article about using this.
For rendering the component server-side, you will need a DOM implementation. [JSDOM](https://github.com/jsdom/jsdom) or [LinkeDOM](https://github.com/WebReflection/linkedom) will do. I feel I should also write an in-depth article about this.

0 comments on commit 081a6bc

Please sign in to comment.