npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

uragaki-ui

v0.1.7

Published

Hand-drawn UI components built on shadcn/ui + rough.js

Readme

uragaki-ui

PoC開発専用のペーパープロトタイピングライクUI

shadcn/ui と rough.js をベースにした手書き風UIコンポーネントライブラリです。 プロトタイピング、ワイヤーフレーム作成、遊び心のあるアプリケーションに最適なスケッチスタイルのコンポーネントを提供します。

インストール

npm install uragaki-ui

4. セットアップ

// スタイルのインポート(必須)
import "uragaki-ui/styles.css";

// ThemeProvider でアプリをラップ(ダークモード対応)
import { ThemeProvider } from "uragaki-ui";

function App() {
  return (
    <ThemeProvider defaultTheme="system">
      {/* アプリの中身 */}
    </ThemeProvider>
  );
}

ピア依存関係

react >= 18, react-dom >= 18, tailwindcss >= 4

使い方

すべてのコンポーネントは uragaki-ui からインポートします。 shadcn/ui と同じAPIに Uragaki プレフィックスを付けた名前です。

Button

import { UragakiButton } from "uragaki-ui";

<UragakiButton variant="default">Primary</UragakiButton>
<UragakiButton variant="destructive">Delete</UragakiButton>
<UragakiButton variant="outline">Outline</UragakiButton>
<UragakiButton variant="secondary">Secondary</UragakiButton>
<UragakiButton variant="ghost">Ghost</UragakiButton>
<UragakiButton variant="link">Link</UragakiButton>
<UragakiButton size="sm">Small</UragakiButton>
<UragakiButton size="lg">Large</UragakiButton>
<UragakiButton size="icon"><Icon /></UragakiButton>

Props: variant ("default" | "destructive" | "outline" | "secondary" | "ghost" | "link"), size ("default" | "sm" | "lg" | "icon"), asChild, roughness (default: 2.6), seed

Input

import { UragakiInput } from "uragaki-ui";

<UragakiInput placeholder="名前" />
<UragakiInput borderStyle="box" placeholder="ボックス型" />
<UragakiInput type="password" placeholder="パスワード" />

Props: borderStyle ("underline" | "box", default: "underline"), roughness, seed, + 標準 input 属性すべて

Textarea

import { UragakiTextarea } from "uragaki-ui";

<UragakiTextarea placeholder="メッセージを入力..." rows={4} />

Props: roughness, seed, + 標準 textarea 属性すべて

Card

import {
  UragakiCard, UragakiCardHeader, UragakiCardTitle,
  UragakiCardDescription, UragakiCardContent, UragakiCardFooter
} from "uragaki-ui";

<UragakiCard>
  <UragakiCardHeader>
    <UragakiCardTitle>タイトル</UragakiCardTitle>
    <UragakiCardDescription>説明文</UragakiCardDescription>
  </UragakiCardHeader>
  <UragakiCardContent>
    <p>カード内容</p>
  </UragakiCardContent>
  <UragakiCardFooter>
    <UragakiButton>アクション</UragakiButton>
  </UragakiCardFooter>
</UragakiCard>

サブコンポーネント: UragakiCardHeader, UragakiCardTitle, UragakiCardDescription, UragakiCardContent, UragakiCardFooter, UragakiCardAction

Badge

import { UragakiBadge } from "uragaki-ui";

<UragakiBadge>Default</UragakiBadge>
<UragakiBadge variant="destructive">Error</UragakiBadge>
<UragakiBadge variant="secondary">Info</UragakiBadge>
<UragakiBadge variant="outline">Outline</UragakiBadge>

Props: variant ("default" | "destructive" | "secondary" | "outline"), roughness, seed

Alert

import { UragakiAlert, UragakiAlertTitle, UragakiAlertDescription } from "uragaki-ui";

<UragakiAlert>
  <UragakiAlertTitle>お知らせ</UragakiAlertTitle>
  <UragakiAlertDescription>内容はここに。</UragakiAlertDescription>
</UragakiAlert>

<UragakiAlert variant="destructive">
  <UragakiAlertTitle>エラー</UragakiAlertTitle>
  <UragakiAlertDescription>問題が発生しました。</UragakiAlertDescription>
</UragakiAlert>

Props: variant ("default" | "destructive"), roughness, seed

Checkbox

import { UragakiCheckbox } from "uragaki-ui";
import { UragakiLabel } from "uragaki-ui";

<div className="flex items-center gap-2">
  <UragakiCheckbox id="terms" />
  <UragakiLabel htmlFor="terms">利用規約に同意する</UragakiLabel>
</div>

RadioGroup

import { UragakiRadioGroup, UragakiRadioGroupItem } from "uragaki-ui";
import { UragakiLabel } from "uragaki-ui";

<UragakiRadioGroup defaultValue="opt1">
  <div className="flex items-center gap-2">
    <UragakiRadioGroupItem value="opt1" id="opt1" />
    <UragakiLabel htmlFor="opt1">選択肢1</UragakiLabel>
  </div>
  <div className="flex items-center gap-2">
    <UragakiRadioGroupItem value="opt2" id="opt2" />
    <UragakiLabel htmlFor="opt2">選択肢2</UragakiLabel>
  </div>
</UragakiRadioGroup>

Select

import {
  UragakiSelect, UragakiSelectTrigger, UragakiSelectContent,
  UragakiSelectItem, UragakiSelectValue
} from "uragaki-ui";

<UragakiSelect>
  <UragakiSelectTrigger className="w-[200px]">
    <UragakiSelectValue placeholder="選択..." />
  </UragakiSelectTrigger>
  <UragakiSelectContent>
    <UragakiSelectItem value="apple">りんご</UragakiSelectItem>
    <UragakiSelectItem value="banana">バナナ</UragakiSelectItem>
    <UragakiSelectItem value="cherry">さくらんぼ</UragakiSelectItem>
  </UragakiSelectContent>
</UragakiSelect>

サブコンポーネント: UragakiSelectTrigger, UragakiSelectContent, UragakiSelectItem, UragakiSelectValue, UragakiSelectGroup, UragakiSelectLabel

Switch

import { UragakiSwitch } from "uragaki-ui";

<div className="flex items-center gap-2">
  <UragakiSwitch id="notifications" />
  <UragakiLabel htmlFor="notifications">通知</UragakiLabel>
</div>

Tabs

import { UragakiTabs, UragakiTabsList, UragakiTabsTrigger, UragakiTabsContent } from "uragaki-ui";

<UragakiTabs defaultValue="tab1">
  <UragakiTabsList>
    <UragakiTabsTrigger value="tab1">タブ1</UragakiTabsTrigger>
    <UragakiTabsTrigger value="tab2">タブ2</UragakiTabsTrigger>
  </UragakiTabsList>
  <UragakiTabsContent value="tab1">タブ1の内容</UragakiTabsContent>
  <UragakiTabsContent value="tab2">タブ2の内容</UragakiTabsContent>
</UragakiTabs>

Accordion

import { UragakiAccordion, UragakiAccordionItem, UragakiAccordionTrigger, UragakiAccordionContent } from "uragaki-ui";

<UragakiAccordion type="single" collapsible>
  <UragakiAccordionItem value="item-1">
    <UragakiAccordionTrigger>セクション1</UragakiAccordionTrigger>
    <UragakiAccordionContent>内容1</UragakiAccordionContent>
  </UragakiAccordionItem>
  <UragakiAccordionItem value="item-2">
    <UragakiAccordionTrigger>セクション2</UragakiAccordionTrigger>
    <UragakiAccordionContent>内容2</UragakiAccordionContent>
  </UragakiAccordionItem>
</UragakiAccordion>

Props: type ("single" | "multiple"), collapsible (boolean), roughness, seed

Dialog

import {
  UragakiDialog, UragakiDialogTrigger, UragakiDialogContent,
  UragakiDialogHeader, UragakiDialogFooter, UragakiDialogTitle,
  UragakiDialogDescription, UragakiButton
} from "uragaki-ui";

<UragakiDialog>
  <UragakiDialogTrigger asChild>
    <UragakiButton variant="outline">ダイアログを開く</UragakiButton>
  </UragakiDialogTrigger>
  <UragakiDialogContent>
    <UragakiDialogHeader>
      <UragakiDialogTitle>タイトル</UragakiDialogTitle>
      <UragakiDialogDescription>説明文</UragakiDialogDescription>
    </UragakiDialogHeader>
    <UragakiDialogFooter>
      <UragakiButton>保存</UragakiButton>
    </UragakiDialogFooter>
  </UragakiDialogContent>
</UragakiDialog>

AlertDialog

import {
  UragakiAlertDialog, UragakiAlertDialogTrigger, UragakiAlertDialogContent,
  UragakiAlertDialogHeader, UragakiAlertDialogFooter, UragakiAlertDialogTitle,
  UragakiAlertDialogDescription, UragakiAlertDialogAction, UragakiAlertDialogCancel
} from "uragaki-ui";

<UragakiAlertDialog>
  <UragakiAlertDialogTrigger asChild>
    <UragakiButton variant="destructive">削除</UragakiButton>
  </UragakiAlertDialogTrigger>
  <UragakiAlertDialogContent>
    <UragakiAlertDialogHeader>
      <UragakiAlertDialogTitle>本当に削除しますか?</UragakiAlertDialogTitle>
      <UragakiAlertDialogDescription>この操作は取り消せません。</UragakiAlertDialogDescription>
    </UragakiAlertDialogHeader>
    <UragakiAlertDialogFooter>
      <UragakiAlertDialogCancel>キャンセル</UragakiAlertDialogCancel>
      <UragakiAlertDialogAction>削除</UragakiAlertDialogAction>
    </UragakiAlertDialogFooter>
  </UragakiAlertDialogContent>
</UragakiAlertDialog>

Sheet

import {
  UragakiSheet, UragakiSheetTrigger, UragakiSheetContent,
  UragakiSheetHeader, UragakiSheetTitle, UragakiSheetDescription
} from "uragaki-ui";

<UragakiSheet>
  <UragakiSheetTrigger asChild>
    <UragakiButton variant="outline">パネルを開く</UragakiButton>
  </UragakiSheetTrigger>
  <UragakiSheetContent>
    <UragakiSheetHeader>
      <UragakiSheetTitle>設定</UragakiSheetTitle>
      <UragakiSheetDescription>設定を管理します。</UragakiSheetDescription>
    </UragakiSheetHeader>
    <div className="py-4">内容</div>
  </UragakiSheetContent>
</UragakiSheet>

Drawer

import {
  UragakiDrawer, UragakiDrawerTrigger, UragakiDrawerContent,
  UragakiDrawerHeader, UragakiDrawerTitle, UragakiDrawerDescription,
  UragakiDrawerFooter, UragakiDrawerClose
} from "uragaki-ui";

<UragakiDrawer>
  <UragakiDrawerTrigger asChild>
    <UragakiButton variant="outline">ドロワーを開く</UragakiButton>
  </UragakiDrawerTrigger>
  <UragakiDrawerContent>
    <UragakiDrawerHeader>
      <UragakiDrawerTitle>メニュー</UragakiDrawerTitle>
      <UragakiDrawerDescription>オプションを選択</UragakiDrawerDescription>
    </UragakiDrawerHeader>
    <div className="p-4">内容</div>
    <UragakiDrawerFooter>
      <UragakiDrawerClose asChild>
        <UragakiButton variant="outline">閉じる</UragakiButton>
      </UragakiDrawerClose>
    </UragakiDrawerFooter>
  </UragakiDrawerContent>
</UragakiDrawer>

Popover

import { UragakiPopover, UragakiPopoverTrigger, UragakiPopoverContent } from "uragaki-ui";

<UragakiPopover>
  <UragakiPopoverTrigger asChild>
    <UragakiButton variant="outline">情報</UragakiButton>
  </UragakiPopoverTrigger>
  <UragakiPopoverContent className="w-80">
    <p>ポップオーバーの内容</p>
  </UragakiPopoverContent>
</UragakiPopover>

Tooltip

import { UragakiTooltipProvider, UragakiTooltip, UragakiTooltipTrigger, UragakiTooltipContent } from "uragaki-ui";

<UragakiTooltipProvider>
  <UragakiTooltip>
    <UragakiTooltipTrigger asChild>
      <UragakiButton variant="outline">ホバー</UragakiButton>
    </UragakiTooltipTrigger>
    <UragakiTooltipContent>
      <p>ツールチップ</p>
    </UragakiTooltipContent>
  </UragakiTooltip>
</UragakiTooltipProvider>

DropdownMenu

import {
  UragakiDropdownMenu, UragakiDropdownMenuTrigger, UragakiDropdownMenuContent,
  UragakiDropdownMenuItem, UragakiDropdownMenuLabel, UragakiDropdownMenuSeparator
} from "uragaki-ui";

<UragakiDropdownMenu>
  <UragakiDropdownMenuTrigger asChild>
    <UragakiButton variant="outline">メニュー</UragakiButton>
  </UragakiDropdownMenuTrigger>
  <UragakiDropdownMenuContent>
    <UragakiDropdownMenuLabel>操作</UragakiDropdownMenuLabel>
    <UragakiDropdownMenuSeparator />
    <UragakiDropdownMenuItem>編集</UragakiDropdownMenuItem>
    <UragakiDropdownMenuItem>複製</UragakiDropdownMenuItem>
    <UragakiDropdownMenuSeparator />
    <UragakiDropdownMenuItem>削除</UragakiDropdownMenuItem>
  </UragakiDropdownMenuContent>
</UragakiDropdownMenu>

Table

import {
  UragakiTable, UragakiTableHeader, UragakiTableBody,
  UragakiTableRow, UragakiTableHead, UragakiTableCell
} from "uragaki-ui";

<UragakiTable>
  <UragakiTableHeader>
    <UragakiTableRow>
      <UragakiTableHead>名前</UragakiTableHead>
      <UragakiTableHead>メール</UragakiTableHead>
    </UragakiTableRow>
  </UragakiTableHeader>
  <UragakiTableBody>
    <UragakiTableRow>
      <UragakiTableCell>太郎</UragakiTableCell>
      <UragakiTableCell>[email protected]</UragakiTableCell>
    </UragakiTableRow>
  </UragakiTableBody>
</UragakiTable>

Form (react-hook-form + zod)

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
  UragakiForm, UragakiFormField, UragakiFormItem,
  UragakiFormLabel, UragakiFormControl, UragakiFormMessage,
  UragakiInput, UragakiButton
} from "uragaki-ui";

const schema = z.object({ name: z.string().min(1) });

function MyForm() {
  const form = useForm({ resolver: zodResolver(schema) });
  return (
    <UragakiForm {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)}>
        <UragakiFormField
          control={form.control}
          name="name"
          render={({ field }) => (
            <UragakiFormItem>
              <UragakiFormLabel>名前</UragakiFormLabel>
              <UragakiFormControl>
                <UragakiInput {...field} />
              </UragakiFormControl>
              <UragakiFormMessage />
            </UragakiFormItem>
          )}
        />
        <UragakiButton type="submit">送信</UragakiButton>
      </form>
    </UragakiForm>
  );
}

Sonner (トースト通知)

import { UragakiSonner } from "uragaki-ui";
import { toast } from "sonner";

// レイアウトに配置
<UragakiSonner />

// トリガー
toast("イベントを作成しました");
toast.success("成功!");
toast.error("エラーが発生しました");

Avatar

import { UragakiAvatar, UragakiAvatarImage, UragakiAvatarFallback } from "uragaki-ui";

<UragakiAvatar>
  <UragakiAvatarImage src="/avatar.jpg" alt="ユーザー" />
  <UragakiAvatarFallback>YI</UragakiAvatarFallback>
</UragakiAvatar>

Progress

import { UragakiProgress } from "uragaki-ui";

<UragakiProgress value={60} />

Separator

import { UragakiSeparator } from "uragaki-ui";

<UragakiSeparator />
<UragakiSeparator orientation="vertical" className="h-8" />

Props: orientation ("horizontal" | "vertical", default: "horizontal")

Skeleton

import { UragakiSkeleton } from "uragaki-ui";

<UragakiSkeleton className="h-4 w-[250px]" />
<UragakiSkeleton className="h-12 w-12 rounded-full" />

Calendar

import { UragakiCalendar } from "uragaki-ui";

const [date, setDate] = useState<Date | undefined>();
<UragakiCalendar mode="single" selected={date} onSelect={setDate} />

Breadcrumb

import {
  UragakiBreadcrumb, UragakiBreadcrumbList, UragakiBreadcrumbItem,
  UragakiBreadcrumbLink, UragakiBreadcrumbSeparator, UragakiBreadcrumbPage
} from "uragaki-ui";

<UragakiBreadcrumb>
  <UragakiBreadcrumbList>
    <UragakiBreadcrumbItem>
      <UragakiBreadcrumbLink href="/">ホーム</UragakiBreadcrumbLink>
    </UragakiBreadcrumbItem>
    <UragakiBreadcrumbSeparator />
    <UragakiBreadcrumbItem>
      <UragakiBreadcrumbPage>現在のページ</UragakiBreadcrumbPage>
    </UragakiBreadcrumbItem>
  </UragakiBreadcrumbList>
</UragakiBreadcrumb>

Carousel

import { UragakiCarousel, UragakiCarouselContent, UragakiCarouselItem, UragakiCarouselPrevious, UragakiCarouselNext } from "uragaki-ui";

<UragakiCarousel>
  <UragakiCarouselContent>
    <UragakiCarouselItem>スライド1</UragakiCarouselItem>
    <UragakiCarouselItem>スライド2</UragakiCarouselItem>
  </UragakiCarouselContent>
  <UragakiCarouselPrevious />
  <UragakiCarouselNext />
</UragakiCarousel>

Command

import {
  UragakiCommand, UragakiCommandInput, UragakiCommandList,
  UragakiCommandEmpty, UragakiCommandGroup, UragakiCommandItem
} from "uragaki-ui";

<UragakiCommand>
  <UragakiCommandInput placeholder="検索..." />
  <UragakiCommandList>
    <UragakiCommandEmpty>結果なし</UragakiCommandEmpty>
    <UragakiCommandGroup heading="操作">
      <UragakiCommandItem>新規作成</UragakiCommandItem>
      <UragakiCommandItem>設定を開く</UragakiCommandItem>
    </UragakiCommandGroup>
  </UragakiCommandList>
</UragakiCommand>

Pagination

import {
  UragakiPagination, UragakiPaginationContent, UragakiPaginationItem,
  UragakiPaginationLink, UragakiPaginationPrevious, UragakiPaginationNext
} from "uragaki-ui";

<UragakiPagination>
  <UragakiPaginationContent>
    <UragakiPaginationItem><UragakiPaginationPrevious href="#" /></UragakiPaginationItem>
    <UragakiPaginationItem><UragakiPaginationLink href="#" isActive>1</UragakiPaginationLink></UragakiPaginationItem>
    <UragakiPaginationItem><UragakiPaginationLink href="#">2</UragakiPaginationLink></UragakiPaginationItem>
    <UragakiPaginationItem><UragakiPaginationNext href="#" /></UragakiPaginationItem>
  </UragakiPaginationContent>
</UragakiPagination>

共通Props

rough.js を使うコンポーネントは以下の共通プロパティを受け取ります:

| Prop | 型 | デフォルト | 説明 | |------|------|---------|-------------| | roughness | number | 2.6 | 線のラフさ(大きいほど手書き風が強い) | | seed | number | 自動 | 描画の乱数シード(固定するとレンダリングが安定) |

UragakiBorder

任意のコンテンツに手書き風ボーダーを付与するコアラッパーです。

import { UragakiBorder } from "uragaki-ui";

<UragakiBorder shape="rectangle" roughness={2.6} seed={42} fill="#f0f0f0">
  <p>手書き風ボーダー付きコンテンツ</p>
</UragakiBorder>

| Prop | 型 | デフォルト | 説明 | |------|------|---------|-------------| | shape | "rectangle" | "ellipse" | "rectangle" | ボーダーの形状 | | roughness | number | 2.6 | 線のラフさ | | bowing | number | 1.8 | 線のたわみ | | strokeWidth | number | 2 | 線の太さ | | stroke | string | "currentColor" | 線の色 | | fill | string | - | 塗りつぶし色 | | fillStyle | string | "hachure" | 塗りスタイル | | seed | number | 自動 | 乱数シード | | padding | number | 0 | 内側余白 | | block | boolean | false | display: block にするか |

ThemeProvider / useTheme

ダークモード切り替えを管理します。

import { ThemeProvider, useTheme } from "uragaki-ui";

// プロバイダー
<ThemeProvider defaultTheme="system" storageKey="uragaki-ui-theme">
  {children}
</ThemeProvider>

// フック
const { theme, setTheme, resolvedTheme } = useTheme();
setTheme("dark"); // "light" | "dark" | "system"

CSSクラス

| クラス | 説明 | |------|------| | uragaki-paper | 紙テクスチャ背景(24pxグリッドパターン) | | uragaki-hover | ホバー時にぐらぐら揺れるアニメーション | | uragaki-no-border | ボーダーを非表示 |

コンポーネント一覧

| カテゴリ | コンポーネント | |---------|--------------| | 入力 | Button, Input, Textarea, Checkbox, Radio Group, Select, Slider, Switch, Toggle, Toggle Group, Input OTP, Calendar, Form, Label | | 表示 | Card, Badge, Alert, Avatar, Skeleton, Progress, Separator, Aspect Ratio, Chart | | ナビゲーション | Tabs, Breadcrumb, Pagination, Navigation Menu, Menubar, Sidebar, Command, Carousel | | オーバーレイ | Dialog, Alert Dialog, Sheet, Drawer, Popover, Tooltip, Hover Card, Context Menu, Dropdown Menu, Sonner | | レイアウト | Accordion, Collapsible, Resizable, Scroll Area, Table | | ユーティリティ | ThemeProvider, useTheme, UragakiBorder |

開発者向け

事前準備

pnpm install

コマンド一覧

pnpm dev              # 開発サーバー起動
pnpm storybook        # Storybook 起動(コンポーネントカタログ)
pnpm build:lib        # ライブラリビルド
pnpm deploy:pages     # ドキュメントサイト + Storybook を GitHub Pages にデプロイ
pnpm publish:npm <version>  # npm にパッケージを公開

パッケージ公開

pnpm publish:npm patch   # 0.1.0 -> 0.1.1
pnpm publish:npm minor   # 0.1.0 -> 0.2.0
pnpm publish:npm 1.0.0   # 明示的にバージョン指定

特徴

  • rough.js の SVG レンダリングによる手書き風ボーダー
  • 手書き風フォント(Sketchflow Print、Yusei Magic、Hachi Maru Pop)
  • グリッドパターン付きの紙テクスチャ背景
  • shadcn/ui(Radix UI)ベースの完全なアクセシビリティ対応
  • Tailwind CSS v4 によるスタイリング
  • ダークモード対応
  • 安定したシード値レンダリングで描画のちらつきを防止

技術スタック

  • React 19 + TypeScript 5.8
  • Vite + tsup
  • Tailwind CSS v4
  • shadcn/ui (Radix UI)
  • rough.js
  • Storybook 8

ライセンス

MIT