TL;DR
Rebuilt portfolio.
- High Speed
- Scalable
- State-of-the-art technology
https://re-taro.dev <- This is the Portfolio I build!
https://github.com/re-taro/re-taro.dev <- And this is the repository!
High Speed
We are using Next.js as our framework to do SG.
It's frustrating to be Vercel's dog, but I'll put up with it. It is because it is attractive to be able to optimize font and images easily.
Although it is called SG, JS runs at each page transition, so the movement is smooth (in other words, it is not pure).
Cloudinary is used for image management to deliver optimized images.
The size of the images is directly related to the display speed, so it is very helpful.
Scalable
Throughout
The component design is based on Atomic Design, a practice known as Component Driven Development, so it is easy to reuse and extend.
Web Page
In order to keep the information as up-to-date as possible and not to leave direct commits to this repository when changing information, an API server was built and information was exchanged through it.
Blog Page
Markdown (content management) + tsx (template engine); Ramanujan said that Markdown would not go out of date so easily and that he felt comfortable throwing it into another service someday (I don't know, but...).
The Markdown processor used unified assets.
It was fun because it was built using a combination of various unified APIs.
State-of-the-art technology
I like it. Yes, I like state-of-the-art technology. Below is an excerpt from my club's LT talk, listing the technologies used in this portfolio.
Front-end
Next.js for framework, Tailwind CSS for styling (but I wanted to use CSS in JS, so I used twin.macro this time).
I'm using Urql as my GraphQL client.
Hosting is by Vercel (I'm a dog, woof woof).
Back-end
I'm using Juniper. You may have noticed that I am trying to write a GraphQL server in Rust.
It's difficult again. I want to escape to Nest.js right away.
I'm leaving it to render to deploy to (Heroku? That guy is...). I'm still trying to figure out what to do with GraphQL.
GraphQL is a justice, I thought while making it.
Others
I'm trying to use umami this time for analytics. The UI is really cute and I love it.
Feature
In the following, we will describe the specific functions we were able to implement and the libraries we used.
Tailwind CSS & twin.macro
I don't think Tailwind CSS is justice. However, I think it is one of the solutions.
Also, since I adopted Atomic Design, I thought that developing with CSS in JS was the best choice, so I used twin.macro.
There was one major problem with twin.macro. It does not support Tailwind CSS v3.
The issue is still standing and is being monitored.
The flex-basis
and other features added since Tailwind CSS v3 are naturally not supported, so the following action was taken.
Urql
What do you think of GraphQL client libraries? Some well-known ones are Apollo and Relay. There is a library called @next/bundle-analyzer
. Let's build it and look at the bundle size.
You will be surprised at the size of Apollo and Relay. You will be surprised at the size of Apollo and Relay, and how small Urql is in comparison. If you're curious, Urql does a comparison here.
Of course, it is the right size for the right place. In this case, I only needed a query, so I made it as small as possible.
/* pages/index.tsx */
// 略
// eslint-disable-next-line unicorn/prevent-abbreviations
export const getStaticProps: GetStaticProps<{ meta: SeoProperties; urqlState: SSRData }> = async () => {
const client = await urqlClient();
await client.query(HomeDocument).toPromise();
const meta: SeoProperties = {
description: "Rintaro Itokawa's Dev Site | re-taro",
ogImageUrl: encodeURI(`${OGP_HOST}/api/ogp?title=re-taro`),
pageRelPath: "",
pagetype: "website",
sitename: "re-taro.dev",
title: "Rintaro Itokawa - Emotion Seeker",
twcardtype: "summary_large_image",
};
return {
props: {
meta,
urqlState: ssrCache.extractData(),
},
};
};
const HomePage: NextPage<Properties> = ({ meta }) => {
const [response] = useQuery<HomeQuery>({ query: HomeDocument });
return <Home data={response.data} meta={meta} />;
};
export default withUrqlClient(
() => ({
url: END_POINT,
}),
{ neverSuspend: true, ssr: false },
)(HomePage);
It was very simple and easy to use, although a little unusual to use.
GitHub Flavored Markdown
Supported by remark-gfm
.
| Create | Table |
| -------- | ---------- |
| For | Example |
| increase | Element. |
- I can write
- list. Also, [^1]
[^1]: Footnotes can be used.
Create | Table |
---|---|
For | Example |
increase | Element. |
- I can write
- list. Also, 1
Emoji
Convert with remakr-gemoji
.
:v:
convert to ✌️.
Numerical Formula
Bite the remark-math
and rehype-katex
.
It is easy to set up just by loading the style sheet.
// components/organisms/post-meta/index.tsx
// 略
const PostMeta: React.FC<PostMetaPropeties> = ({ meta }) => (
<React.Fragment>
<Seo {...meta} />
<Head>
<link
rel={"stylesheet"}
href={"https://cdn.jsdelivr.net/npm/katex@0.15.6/dist/katex.min.css"}
integrity={"sha384-ljao5I1l+8KYFXG7LNEA7DyaFvuvSCmedUf6Y6JI7LJqiu8q5dEivP2nDdFH31V4"}
crossOrigin={"anonymous"}
/>
<title></title>
</Head>
</React.Fragment>
);
// 略
ruby
I used remark-jaruby created by @haxibami.
The chain of methods around remark-parse
/ remark-rehype
is as follows...
// utils/parser.ts
import rehypeShiki from "@re-taro/rehype-shiki";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypeKatex from "rehype-katex";
import rehypeSlug from "rehype-slug";
import rehypeStringify from "rehype-stringify";
import remarkGemoji from "remark-gemoji";
import remarkGfm from "remark-gfm";
import remarkJaruby from "remark-jaruby";
import remarkMath from "remark-math";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import remarkToc from "remark-toc";
import remarkUnwrapImages from "remark-unwrap-images";
import * as shiki from "shiki";
import stripMarkdown from "strip-markdown";
import { unified } from "unified";
const MdToHtml = async (md: string) => {
const result = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkGemoji)
.use(remarkMath)
.use(remarkJaruby)
.use(remarkUnwrapImages)
.use(remarkToc, {
heading: "目次",
tight: true,
})
.use(remarkRehype)
.use(rehypeKatex)
.use(rehypeShiki, {
highlighter: await shiki.getHighlighter({ theme: "nord" }),
})
.use(rehypeSlug)
.use(rehypeAutolinkHeadings, {
behavior: "wrap",
})
.use(rehypeStringify)
.process(md);
return result.toString();
};
export { MdToHtml }
In addition, the rehype-react
related process is as follows.
// lib/rehype-react.ts
import React from "react";
import rehypeParse from "rehype-parse";
import rehypeReact from "rehype-react";
import type { Options as RehypeReactOptions } from "rehype-react";
import { unified } from "unified";
import { Image } from "~/components/molecules/image";
import type { ImageProperties } from "~/components/molecules/image";
import { Link } from "~/components/molecules/link";
import type { LinkProperties } from "~/components/molecules/link";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const RehypeReact = (html: string): React.ReactElement<unknown, string | React.JSXElementConstructor<any>> => {
const result = unified()
.use(rehypeParse, {
fragment: true,
})
.use(rehypeReact, {
components: {
// eslint-disable-next-line id-length
a: (properties: LinkProperties) => Link(properties),
img: (properties: ImageProperties) => Image(properties),
},
createElement: React.createElement,
} as RehypeReactOptions)
.processSync(html);
return result.result;
};
export { RehypeReact };
The above made the blog very easy to write.
dynamic OGP
I deployed to Vercel while looking at @haxibami's implementation.
https://github.com/re-taro/ogp.re-taro.dev <- This is the repository!
CI/CD
When the repository where the data is stored is updated, actions fires and sends a dispatch event to the portfolio repository, and the one that receives the dispatch event automatically builds the data. I like it, it's quite stylish.
Dispatcher
name: Dispatch
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: |
curl -vv -H "Authorization: token ${{ secrets.DISPATCH_TOKEN }}" -H "Accept: application/vnd.github.everest-preview+json" "https://api.github.com/repos/re-taro/re-taro.dev/dispatches" -d '{"event_type": "update"}'
Receiver of the dispatch event
name: Dispatch production build
on:
repository_dispatch:
types: [update]
jobs:
// Jobs you want to run
Impressions
I am proud to say that it turned out crazy good.
-
Footnotes can be used. ↩
Top comments (0)