125k

Typeset

ブログ投稿からストリーミングチャットまで、HTML およびレンダリングされたマークダウンのスタイルシステム。あなたが所有する 1 つの CSS ファイル。

マークダウンをレンダリングすると、見出し、段落、リスト、表など、スタイルのないプレーンな HTML が返されます。そのため、フォントサイズ、行の高さ、間隔などの要素を 1 つずつスタイル設定することになります。

まずブログでそれを行います。次に、ドキュメントでも同じ作業を繰り返します。そしてチャットアプリでも同様です。毎回、同じサイズと間隔の問題に苦しめられます。

これを修正するために、shadcn/typeset を作成しました。これは、typeset コンテナ内のすべてのスタイルを設定する 1 つの CSS ファイルです。ファイルはプロジェクト内に存在するため、必要なときに直接変更できます。

タイプセットは、単なる小さなプリセットクラスです。さまざまなコンテキストに合わせて、アプリ内に複数のタイプセットを含めることができます。

.typeset-docs {
  --typeset-font-body: var(--font-geist);
  --typeset-font-heading: var(--font-geist);
  --typeset-font-mono: var(--font-geist-mono);
  --typeset-size: 15px;
  --typeset-leading: 1.75;
  --typeset-flow: 1.25em;
}
タイプセットを構築する

原則

私たちは、スケール比率、トラッキング、カーニング、光学サイジング、メジャー、行送り、各要素の上下のスペースなど、タイポグラフィについて多くのことを読み込みました。すべてを設定可能にしてみましたが、それはやりすぎでした。マークダウンを正しく見せるために多数の変数を設定したいと思う人はいません。

そこで私たちは、すべてをサイズ、リーディング、フローという 3 つのコントロールに凝縮しました。見出しのサイズ、リストのインデント、見出しの下の隙間、ルールの周囲のスペースなど、他のすべてはこの 3 つのコントロールから派生します。私たちはこれをリズムと呼んでいます。


特徴

  • コンテナに適合します。 チャットバブル内に配置すると、周囲の小さな文字サイズに合わせます。記事に挿入すると、ページに合わせて拡大されます。小さな画面では、読みやすさのためにわずかに大きくなります。
  • テーマを使用します。 色、フォント、半径はアプリから取得されます。ダークモードは同じトークンに従います。
  • 調整は簡単です。 3 つの値で、ベースサイズ、行の高さ、ブロック間のスペースを制御します。プリセットでそれらを変更すると、ドキュメント全体がそれに追従します。
  • ストリーミングでうまく機能します。 新しいブロックが到着しても、タイプセットは以前のブロックのマージン、境界線、またはスタイルを切り替えません。

タイプセットを構築する

タイプセットビルダー でタイプセットを作成します。フォントとリズムを選択し、ドキュメント、チャット、記事、その他の実際のコンテンツでプレビューします。

このパネルには、typeset.css ファイル、フレームワークのフォント設定、選択したプリセットクラス、およびコンテンツの周囲に追加するラッパーが表示されます。

メイン CSS ファイルの隣に typeset.css をコピーし、Tailwind の後にインポートします。

@import "tailwindcss";
@import "./typeset.css";

次に、レンダリングされたマークダウンを typeset とプリセットクラスでラップします。

<div className="typeset typeset-docs">
  <YourMarkdownRenderer>{content}</YourMarkdownRenderer>
</div>

typeset はスタイルをオンにします。 typeset-docs は、ビルダーで作成したプリセットです。


カスタムタイプセット

ファイルにはデフォルトが含まれているため、typeset を単独で使用できます。読書リズムのほとんどは、次の 3 つの値から決まります。

.typeset {
  --typeset-font-body: inherit;
  --typeset-font-heading: var(--font-heading);
  --typeset-font-mono: var(--font-mono);
 
  --typeset-size: 1em; /* body font-size */
  --typeset-leading: 1.75; /* line-height */
  --typeset-flow: 1.25em; /* space between blocks */
}
  • --typeset-size は、基本テキストサイズを設定します。1em は周囲のレイアウトに従います。小さい画面では、タイプセットを使用すると少し大きくなります。
  • --typeset-leading は行間のスペースを設定します。
  • --typeset-flow はブロック間のスペースを設定します。見出しやその他の要素の間隔はそこから導き出されます。

フォント変数は、どのファミリを使用するかをタイプセットに指示します。放っておけば、アプリに従います。色と半径もテーマから決まります。

タイプセットは最大幅を設定しません。それはレイアウト側が管理します。ビルダーの Measure コントロールは、スタイルシート内で max-width を非表示にするのではなく、ラッパーに max-width を追加します。

同じアプリ内に複数のプリセットを保持できます。以下はチャット用に狭いもの、ドキュメント用に広いものです。

.typeset-chat {
  --typeset-flow: 1em;
  --typeset-leading: 1.6;
}
 
.typeset-docs {
  --typeset-size: 15px;
  --typeset-flow: 1.5em;
}
<div className="typeset typeset-chat">{message}</div>
<article className="typeset typeset-docs">{page}</article>

1 回限りの変更の場合は、プリセットをスキップして、コンテナーに値を設定します。

<article className="typeset [--typeset-flow:1.75em]">...</article>

カスタムテーマ

プリセットは、間隔だけでなく、コンテンツ全体の雰囲気を変えることができます。読者にセリフ読み取りモード、コンパクト UI モード、または製品に適したその他のスタイルを提供できます。

/* Reading: serif, larger type, roomy rhythm. */
.typeset-reading {
  --typeset-font-body: var(--font-lora);
  --typeset-font-heading: var(--font-lora);
  --typeset-size: 18px;
  --typeset-leading: 1.9;
  --typeset-flow: 2em;
}
 
/* Compact: sans, smaller type, tighter rhythm. */
.typeset-compact {
  --typeset-font-body: var(--font-geist);
  --typeset-font-heading: var(--font-geist);
  --typeset-size: 14px;
  --typeset-leading: 1.6;
  --typeset-flow: 1em;
}

アクセシビリティとダークモード

より大きな活字とより多くのスペースを好む読者の場合は、より広々としたタイプセットを作成し、それを設定として公開します。

.typeset-large {
  --typeset-size: 16px;
  --typeset-leading: 2;
  --typeset-flow: 2em;
}

ダークモードはすでにテーマカラーに従っています。暗い表面上でテキストが少しきつく感じる場合は、その部分の行送りを緩めることができます。

.dark .typeset {
  --typeset-leading: 1.9;
}

レスポンシブテーブル

テーブルは実際のテーブルのままで、ぴったりと収まるように折り返されます。幅の広いものを水平方向にスクロールするには、それを typeset-scroll で囲みます。

<div className="typeset-scroll">
  <table>...</table>
</div>

これをレンダラーのテーブルコンポーネントまたは小さな rehype プラグインで実行します。テーブルだけでなく、あらゆるワイドブロックに対して機能します。


オーバーライド

タイプセットは components レイヤーに存在し、要素セレクターに :where() を使用します。要素の Tailwind ユーティリティは、!important なしで勝ちます。

<div className="typeset typeset-docs">
  <p className="text-lg">...</p>
</div>

プレーン CSS は、通常のセレクターでタイプセットをオーバーライドすることもできます。


オプトアウトする

コンポーネントをタイプセットから除外するには、not-typeset または data-not-typeset を追加します。

<div className="typeset">
  <p>Styled prose.</p>
  <Card className="not-typeset">Untouched component.</Card>
</div>

どちらのオプションも、コンポーネントとその内部のすべてをカバーします。そのサブツリー内の別の typeset コンテナもオプトアウトされたままになります。


ストリーミング

タイプセットは、新しいブロックを追加しても、すでに画面上にあるブロックのスタイルが変更されないように書かれています。

  • 将来を見据えたセレクターはありません。:last-child、:has()、および :empty は、コンテンツが追加されると一致が変わる可能性があるため、レイアウトルールから除外されます。
  • スペースは一方向に流れます。margin-block-start のみを使用します。新しいブロックは独自のスペースを追加します。
  • テーブル区切り記号は追加されるセルに存在するため、新しい行によってその上の行のスタイルが変更されることはありません。

ストリーミング中のテキストは、通常どおり拡大したり折り返すことができます。タイプセットは、その前にあるブロックのスタイル変更を回避するだけです。


従来技術

@tailwindcss/typography の prose クラスは、Markdown や CMS からレンダリングされたコンテンツを含む、プレーンな HTML に美しい文字体裁のデフォルトを追加するという目的で構築された点で優れています。

タイプセットは、コンテナーを意識したサイジング、アプリのテーマトークン、さまざまなコンテキストのプリセット、ストリーミングの安定性に関して、異なるアプローチを採用しています。異なる点は次のとおりです。

@tailwindcss/typographyタイプセット
サイズrem スケール、prose-sm から prose-2xl を修正コンテナーに対して、任意のサイズ
ダークモードprose-invert、2 番目のパレットトークンが反転します。何も追加する必要はありません
テーマ散文の色の変数。焼き付けられたスケールテーマトークン、フォントおよびリズムコントロール
オーバーライドprose-a:、prose-headings: 修飾子 API普通のユーティリティと CSS が勝利
ストリーミング追加安定性契約なし安定した追加用に設計
配布npm プラグイン、生成された CSSあなたが所有する 1 つの CSS ファイル

タイプセットは、プラグインから 2 つの最良のアイデアを借用しています。1 つは特異性ゼロの :where() ガードパターン、もう 1 つはエスケープハッチクラス (not-prose の精神に基づく not-typeset) です。