Components
@kkhys/ui のbase.css
Button
components.css の<button><a>--outline--ghost / --size-icon / --size-sm の
- 使う
- フォーム送信や
投稿など、 画面内で 1 つだけの 主操作は 塗り。 並ぶ副 操作は --outline、取り消しは --ghost。 - 使わない
- ナビゲーションの
遷移先(リンクは 下線か テキストの まま)。 塗りボタンを 1 画面に 2 つ以上 並べない。 - 配慮
- 高さ 2.25rem は
タップ領域の 下限。 disabledは pointer-events: noneなので、理由は ボタンの 外に 書く。
{/* classes: @kkhys/styles/components.css */}
<button class="btn">Post</button>
<button class="btn btn--outline">Sync</button>
<button class="btn btn--ghost">Cancel</button>
<button class="btn btn--ghost btn--size-sm">Reply</button>Spinner
無限スクロールの--c-sub を
- 使う
- 次ページの
取得中だけ。 最低 500ms は 表示して チラつきを 防ぐ。 - 使わない
- 初回描画や
ボタン内。 終端と 失敗は スピナーではなく テキストで 告げる。
import Spinner from "@kkhys/ui/spinner.astro";
<Spinner />BudouX
スロットの<wbr> 区切りに.budouxword-break: keep-all)と
- 使う
- 見出し・リード・
カードの タイトルなど、 折り返し位置が 目立つ短い 日本語。 - 使わない
- 長文本文(me は
rehype で 自動適用)、 <pre>/ <code>、ボタンの ラベル。
Budoux なしあらゆる現実をすべて自分のほうへねじ曲げたのだと思われるかもしれない。
Budoux ありあらゆる
import Budoux from "@kkhys/ui/budoux.astro";
<h1 class="budoux"><Budoux>タイトル</Budoux></h1>LinkCard
OG メタデータ付きの
- 使う
- 本文中で
単独の 段落と して 置く 外部参照。 メタデータは ビルド時に 取得済みの ものを 渡す。 - 使わない
- 文中の
インラインリンク、 同一サイト内の 相互リンクの 羅列。 - 配慮
- favicon は
取得失敗時に globe へ、 og:image は 取得失敗時に プレースホルダなしへ 自動で 落ちる。
import LinkCard from "@kkhys/ui/link-card.astro";
<LinkCard
href={url}
title={title}
description={description}
imageSrc={image?.src}
favicon={favicon}
external
/>SiteHeader
小さなhrefshowBackactions
- 使う
- ページ数の
少ない サイトの トップ chrome。 下層ページは showBackでホームへ 戻る 導線を 出す。 - 使わない
- 複数の
主要ナビを 持つサイト(me は 独自の site-nav)。
import SiteHeader from "@kkhys/ui/site-header.astro";
<SiteHeader title="LGTM" href="/" />
<SiteHeader title="Thread" showBack />
<SiteHeader title="Thread" showBack>
<button slot="actions" type="button">検索</button>
</SiteHeader>SiteFooter
中央寄せのlinksauthorUrl
- 使う
- 本文カラム末尾の
© 表記。 法務ページが あれば linksに並べる。 - 使わない
- サイトマップ代わりの
リンク集。
import SiteFooter from "@kkhys/ui/site-footer.astro";
<SiteFooter
author="Keisuke Hayashi"
authorUrl="https://kkhys.me"
links={[{ href: "/privacy", label: "Privacy Policy" }]}
/>BlurImage
blur-up 付きの<Picture>(memo / diary / art)。blur(36px)
- 使う
- コンテンツ画像全般。
radius/ framedで枠、 transitionNameで 一覧と 詳細を つなぐ。 - 使わない
- アイコンや
アバターな ど 小さな 画像、 CSS 背景。 - 配慮
- 最初の
1 枚は priority(eager + fetchpriority high)。JS 無効時は BlurLoadNoscriptがぼかしを 解く。

import BlurImage from "@kkhys/ui/blur-image.astro";
import photo from "#/assets/photo.jpg";
<BlurImage
src={photo}
alt="..."
widths={[640, 1280]}
sizes="(min-width: 640px) 640px, 100vw"
radius="0.5rem"
framed
priority
/>SearchDialog
Pagefind 検索の@kkhys/searchがh2 の
- 使う
- ページ数が
多く、 目次だけでは 辿れない サイト。 トリガーは commandforのボタンか ⌘K / Ctrl+K(単キーの ショートカットは 持たない)。 - 使わない
- 数ページの
サイト、 フィルタ 1 つで 済む一覧。 - 配慮
- IME 変換中は
検索しない、 Esc は 入力を 保持したまま 閉じる、 状態行は role=statusのまま 残す。
import SearchDialog from "@kkhys/search/dialog.astro";
<SearchDialog id="search-dialog" label="記事を検索" placeholder="記事を検索">
<div slot="filters">...</div>
<Fragment slot="template">
<template class="search-result-template">...</template>
</Fragment>
</SearchDialog>
<script>
import { mountSearchDialog } from "@kkhys/search/dialog-setup";
mountSearchDialog("search-dialog", { bundlePath: "/pagefind/", noun: "記事", renderResult, ... });
</script>InfiniteScroll
次ページをPaginatedGuard/preview/infinite-scroll/2
- 使う
- 時系列で
流し読みする フィード。 ページ境界に 意味が なく、 途中の 位置を 人に 伝える 必要が ない もの。 - 使わない
- 検索結果や
一覧 表など 位置を 指して 参照する もの、 フッターへ 辿り着く 必要が ある ページ。 - 配慮
- 読み込み中は
aria-busy、終端は role=status、失敗は role=alertで告げる。 スクロール以外の 再試行手段は ないので、 失敗文言には 再読み込みの 含みを 持たせる。
- #1
朝の光が机の端まで届く頃、ようやくコーヒーが冷めた。
- #2
駅前の書店が改装していて、児童書の棚が入口に移っていた。
- #3
傘を忘れた日に限って、帰り道だけ雨が降る。
- #4
古い写真の裏に、鉛筆で日付が書いてあった。
- #5
夜の公園でラジオの音が遠くから聞こえてくる。
- #6
雑貨屋で買った青いマグカップは、思ったより重かった。
import InfiniteScroll from "@kkhys/ui/infinite-scroll.astro";
import PaginatedGuard from "@kkhys/ui/paginated-guard.astro";
{page.currentPage > 1 && <PaginatedGuard basePath="/" />}
<div id="feed" data-current-page={page.currentPage} data-total-pages={page.lastPage}>
...
</div>
{page.currentPage < page.lastPage && (
<InfiniteScroll containerId="feed" errorMessage="Failed to load more posts" />
)}Focus ring
キーボードフォーカス(Tab)でoutline
:focus-visible {
outline: var(--ring-width) solid var(--ring);
outline-offset: var(--ring-offset);
}Scrollbar
ページ・要素とも--c-scrollbar)。
あらゆる
あらゆる現実をすべて自分のほうへねじ曲げたのだと思われるかもしれない。あらゆる現実をすべて自分のほうへねじ曲げたのだと思われるかもしれない。
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background: var(--c-scrollbar);
border-radius: 4px;
}