ブログサイトの調整
2024年9月15日

ブログサイトの調整

6,126文字(読了まで約16分)
headerimage

先日作成したブログサイトについて、なんのスタイル設定もしていなかったため、設定を試みます。

以下を設定しました。

  • 各種スタイル設定
  • 目次作成
  • 記事内ソースコードのシンタックスハイライト

Hono 公式

  • Hono が提供する css 記述方法を利用して設定しました。

    • テンプレートリテラルで css を記述するようになっています。

      const className = css`
        font-family: sans-serif;
      `;
      return c.render(<div class={className}>xxxxxxx</div>);
    • 組み合わせて定義することも可能です

      const cssBase = css`
        font-size: 1em;
      `;
      const cssExtended = css`
        ${cssBase}
        font-family: sans-serif;
      `;
    • シンタックスハイライトや intellisense のため、VSCode 拡張vscode-styled-componentsを導入することをおすすめします。

  • CSS grid による配置設定

    • grid を用いて、ブログサイトのヘッダなどを設定しました。 grid の使い方はいろんなサイトで解説してくれているのでここでは省略します。
  • ヘッダ・目次等の固定

    • ユーザが画面スクロールしても、ヘッダや目次は画面外に出てしまわないように固定します。
  • mdx コンポーネントをデフォルトから差し替え

    • よくある技術ブログサイトでは、見出しごとにページ内リンクで遷移可能となっています。

      • markdown から生成される heading タグ(h2、h3)について、a タグを中に持たせたカスタムのコンポーネントに差し替えます
      • また、id を与えることで、URL のフラグメントにより遷移可能とします。
      /app/components/mdx/LinkedHeadings.tsx
      import { css } from "hono/css";
      import { JSX } from "hono/jsx";
       
      export const LinkedH2 = ({ children }: { children: string }) => {
        const cssH2Base = css`
          font-size: 1.3em;
        `;
        return LinkedHeading({ as: "h2", cssClass: cssH2Base, children });
      };
       
      export const LinkedH3 = ({ children }: { children: string }) => {
        const cssH3Base = css`
          font-size: 1.1em;
        `;
        return LinkedHeading({ as: "h3", cssClass: cssH3Base, children });
      };
       
      const LinkedHeading = ({
        as: CustomTag,
        cssClass,
        children,
      }: {
        as: keyof JSX.IntrinsicElements;
        cssClass: Promise<string>;
        children: string;
      }) => {
        const value = children;
       
        // heading内に配置するa要素のスタイル
        const cssInnerAnchor = css`
          color: inherit;
          text-decoration: none;
          &:hover {
            text-decoration: underline;
            &::after {
              content: "  🔗";
              font-size: 0.2em;
              vertical-align: top;
            }
          }
        `;
       
        // ページ内リンクで遷移時に、表示する位置をページ上部からずらす
        const cssHeadingBase = css`
          padding-top: 57px;
          margin-top: -57px;
        `;
       
        const cssHr = css`
          border-style: solid;
        `;
       
        const cssHeadingTL = css`
          ${cssHeadingBase}
          ${cssClass}
          view-timeline-name: ${value ? "--" + value.replace(/\s+/g, "") : ""};
        `;
        return (
          <CustomTag id={value} class={cssHeadingTL}>
            <a class={cssInnerAnchor} href={`#${value}`}>
              {children}
            </a>
            <hr class={cssHr} />
          </CustomTag>
        );
      };
  • 定義したカスタムコンポーネントを mdx から生成されるデフォルトのコンポーネントから差し替えるため、以下を設定します

/app/components/mdx/index.ts
import type { MDXComponents } from "mdx/types";
import { LinkedH2, LinkedH3 } from "./LinkedHeadings";
 
export function useMDXComponents(): MDXComponents {
  return {
    h2: LinkedH2,
    h3: LinkedH3,
  };
}
  • 上記 index.ts は vite の設定として参照します。
vite.config.ts
・・・略・・・
      plugins: [
        ・・・略・・・
        mdx({
          jsxImportSource: "hono/jsx",
          remarkPlugins: [remarkFrontmatter, remarkMdxFrontmatter],
          /**
           * カスタムmdxコンポーネントのインポート指定
           * ※絶対パスとして指定が必要
           **/
          providerImportSource: path.resolve(__dirname, "./app/components/mdx"),
          ・・・略・・・
        })
      ]
・・・略・・・

表示としてはこのように、マウスホバー時に下線とリンクのマークが出るようになりました

リンク付き見出し

  • 記事内の各見出し(H2 タグ及び H3 タグ)を抽出し、それらへのリンクを生成して目次を作ります。
/app/components/Toc.tsx
import { css } from "hono/css";
import { FC } from "hono/jsx";
import { LinkedH2 } from "./mdxComponents/LinkedHeadings";
 
type TocProps = {
  cssClass: Promise<string>;
  tocRecords: {
    tag: Function;
    id: string;
    text: string;
  }[];
};
 
export const Toc: FC<TocProps> = ({ cssClass, tocRecords }) => {
  const cssRootContainerBase = css`
    border: solid 2px transparent;
    border-radius: 10px;
    padding-right: 1em;
    a {
      color: inherit;
      text-decoration: none;
    }
  `;
 
  const cssTocTitle = css`
    padding-left: 1em;
    font-weight: bold;
  `;
 
  const cssTocUl = css`
    padding-left: 1em;
    &::before {
      content: "";
      position: absolute;
      top: 64px;
      bottom: 24px;
      left: 26px;
      width: 1px;
    }
    li {
      margin-bottom: 0em;
      margin-left: 0;
      line-height: 1.5em;
      text-align: left;
      position: relative;
      list-style: none;
    }
  `;
 
  const cssLiH2 = css`
    font-size: 1em;
    padding-left: 2em;
    font-weight: bold;
    &::before {
      content: "";
      display: block;
      position: absolute;
      top: 6px;
      left: 4px;
      width: 11px;
      height: 11px;
      border: solid 1px;
    }
  `;
 
  const cssLiH3 = css`
    font-size: 0.9em;
    padding-left: 2.2em;
 
    &::before {
      content: "";
      display: block;
      position: absolute;
      top: 7px;
      left: 6px;
      width: 7px;
      height: 7px;
      border: 1px solid;
      z-index: 100;
    }
  `;
 
  const cssRootContainer = css`
    ${cssRootContainerBase}
    ${cssClass}
  `;
 
  return (
    <div class={cssRootContainer}>
      <p class={cssTocTitle}>INDEX</p>
      <ul class={cssTocUl}>
        {tocRecords.map((rec, idx, arr) => {
          const cssLiH2TL = css`
            ${cssLiH2}
          `;
          const cssLiH3TL = css`
            ${cssLiH3}
          `;
          return (
            <li class={rec.tag === LinkedH2 ? cssLiH2TL : cssLiH3TL}>
              <a href={"#" + rec.id}>{rec.text}</a>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

このような感じになります。 目次

ややとりとめのない内容となりましたが・・・

  • Hono 組み込みの CSS in JS の記法を用いて、html のスタイリングを行いました。
  • _renderer.tsx に渡されてくる mdx の情報を元に、H2/H3 タグを抽出し、記事の目次を生成しました。
  • 記事中の H2/H3 タグにリンク可能なよう、ID を付与したカスタムコンポーネントを作成し、mdx で生成されるデフォルトのコンポーネントから差し替えました

- コメント -

    このサイトではcookieを利用して、サイト訪問者の識別に利用します。 cookieの利用に同意いただくことで、サイト訪問者は記事のいいね機能等をご利用いただけます。 なお、サイト運営者はアクセス統計としてcookieの情報を利用する場合があります。