記事の見た目をいじる の履歴(No.2)

更新


プログラミング/svelte

いろいろきれいに見えるようにする

メモ: style の :global について

svelte の style 定義には自動的にコンポーネントインスタンスの id が追加されるため、 そのコンポーネントに直接含まれる(子コンポーネントは除外)エレメントにしか効果を及ぼさない。

コンポーネントを超えて効果を期待する場合には :global を付ける。

https://svelte.jp/docs/svelte-components#style

LANG: html
<style>
	:global(strong) {
		/* これはすべての <strong> に適用されます */
		margin: 0;
	}

	div :global(strong) {
		/* これは「このコンポーネント内の <div> 要素」の中にある
			 「すべての <strong> 要素」に
			 適用されます */
		color: goldenrod;
	}
</style>

上記の例では div に :global がかかっていないため、div にはコンポーネントインスタンスの ID が付き、その下にあることが条件になっている。

「このコンポーネントの子供」に適用したい場合にはこのテクニックが役に立ちそう。

ArticleElement コンポーネントを作成

db の Article と同名だとややこしいことになるようなので ArticleElement とした。

src/routes/articles/ArticleElement.svelte に置くことも考えたのだけれど、 関連コードが増えてフォルダーを掘りたくなる気もしたので

src/lib/components/ArticleElement.svelte にした

LANG: html
<script lang="ts" context="module">
  import { marked } from 'marked';
  import { baseUrl } from 'marked-base-url';
  import markedLinkifyIt from 'marked-linkify-it';
  import { markedHighlight } from 'marked-highlight';
  import hljs from 'highlight.js';
  import markedKatex from 'marked-katex-extension';
  import { page } from '$app/stores';

  import 'highlight.js/styles/stackoverflow-dark.min.css';

  marked.use(baseUrl('https://example.com/folder/'));
  const linkifyItSchemas = {};
  const linkifyItOptions = {};
  marked.use(markedLinkifyIt(linkifyItSchemas, linkifyItOptions));
  marked.use(
    markedHighlight({
      langPrefix: 'hljs language-',
      highlight(code, lang) {
        const language = hljs.getLanguage(lang) ? lang : 'plaintext';
        return hljs.highlight(code, { language }).value;
      },
    })
  );
  marked.use(
    markedKatex({
      throwOnError: false,
    })
  );
</script>

<script lang="ts">
  export let title: string;
  export let author: string;
  export let date: Date;
  export let body: string;
  export let permLink: string;
</script>

<article class="prose max-w-full p-4">
  <h1>{title}</h1>
  <div>
    <span>{author}</span>
    <span>{date.toLocaleString()}</span>
    <span><a href={permLink + '/edit'}>編集</a></span>
  </div>
  <content>
    {@html marked.parse(body)}
  </content>
</article>

src/routes/articles/[...titleOrId]/+page.svelte

LANG: html
<script lang="ts">
  import type { PageData } from './$types';
  import ArticleElement from '$lib/components/ArticleElement.svelte';
  import { encodeTitle } from '../lib';

  export let data: PageData;
</script>

<ArticleElement 
  title={data.article.title}
  body={data.article.body}
  author={data.article.author.name}
  date={data.article.createdAt}
  permLink={data.urlRoot + '/articles/' + encodeTitle(data.article.title)}
/>

見出しのレベルを調整

ブログ記事ではタイトルが h1 なので本文中では一番大きいのが h2 になる。

LANG: ts
  // デフォルトのレンダラ
  const vanillaRenderer = new marked.Renderer();

  // 見出しの階層を1つ下げる
  marked.use({
    renderer: {
      heading(text, level, raw) {
        return vanillaRenderer.heading(text, level + 1, raw);
      }
    }
  });

ベース URL

LANG: ts 
import { baseUrl } from 'marked-base-url';
import { PUBLIC_URL_ROOT } from '$env/static/public';
  ...

  // ベースアドレス
  marked.use(baseUrl(PUBLIC_URL_ROOT));

シンタックスハイライト

痒い所に手が届くよう、marked-highlight を使わず自分でやろう。

$ pnpm rm marked-highlight

Counter: 1015 (from 2010/06/03), today: 1, yesterday: 3