Raycast: 生産性UIの芸術
Raycastが50ms未満の応答時間と個性をどのように達成したか:macOSネイティブデザイン、キーボード優先、拡張機能エコシステム、HUDパターン。Swift実装パターン付き。
Raycast:生産性UIの芸術
「最高のツールは、あなたの邪魔をしない。」— Raycast チーム
Raycastは、パワーユーザーのためにSpotlightを置き換えたmacOSランチャーです。温かみと個性を維持しながら、極限の効率性を追求するデザインの手法を示しています。
Raycastが重要な理由
Raycastは、実用的なソフトウェアが冷たく感じる必要はないことを証明しています。以下を兼ね備えています: - 驚異的な速度(50ms未満で起動) - キーボードファーストのインタラクション - ストアによる拡張性 - 心地よいディテールが生む個性
主な成果: - ランチャーの可能性を再定義 - 開発者ツールにも個性を持たせられることを証明 - 活発な拡張機能エコシステムを構築 - macOSネイティブデザインの新基準を確立
重要なポイント
- 50msが速度のしきい値 - ユーザーは50ms以上のレイテンシを感知する。これをガイドラインではなく、厳格なエンジニアリング制約として扱うこと
- ネイティブプラットフォーム統合が信頼を生む - Vibrancy、SF Symbols、システムアクセントカラー、標準ショートカットがRaycastをmacOSの一部のように感じさせる
- キーボードショートカットは発見可能でなければならない - ショートカットを結果にインラインで表示する(⌘1、⌘2)。ツールチップやドキュメントに隠さない
- パフォーマンスコストなしの個性 - 達成時の紙吹雪、遊び心のある空状態、巧みなコピーがレイテンシを追加せずに喜びを生む
- 拡張機能エコシステムにはデザイン言語が必要 - サードパーティの拡張機能が同じフォームコンポーネント、アクションパネル、ナビゲーションパターンを使用するためネイティブに感じる
コアデザイン原則
1. 即座の応答
Raycastは思考の延長のように感じられる。レイテンシなし。待ち時間なし。
どのように実現しているか: - ネイティブSwift実装(Electronではない) - プリロード済みの拡張機能インデックス - 積極的なキャッシング - 最適化されたレンダリングパイプライン
50msルール: 何かが50ms以上かかると、ユーザーは気づく。Raycastはこれを絶対的な制約として扱っている。
デザインへの示唆: UIを即座に表示し、データは非同期で読み込む - 重い操作に対するスケルトンステート - メインスレッドをブロックしない - ネットワーク呼び出しよりローカル処理を優先する
// Raycastスタイルの即時レスポンスパターン
struct SearchView: View {
@State private var results: [Result] = []
@State private var isLoading = false
var body: some View {
VStack {
// キャッシュデータで即座に表示
ForEach(cachedResults) { result in
ResultRow(result)
}
if isLoading {
// 控えめなローディングインジケーター
ProgressView()
.scaleEffect(0.5)
}
}
.task(id: query) {
isLoading = true
results = await search(query)
isLoading = false
}
}
}
2. 深いmacOS統合
RaycastはmacOSに馴染んでいるように感じられます。プラットフォームの規約を尊重しながら、その限界を押し広げています。
ネイティブな振る舞い: - Vibrancyとブラー(NSVisualEffectView) - システムアクセントカラー - 全体にSF Symbols - ネイティブキーボードショートカット - システムのダーク/ライトモードに対応
実装:
// macOS vibrancy
struct RaycastWindow: View {
var body: some View {
VStack {
// Content
}
.background(.ultraThinMaterial) // Native blur
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
}
}
// システムアクセントカラー
Text("Selected")
.foregroundStyle(.tint) // システム設定に従う
// セマンティックカラーを使用したSF Symbols
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
ウィンドウデザイン:
┌────────────────────────────────────────────────────────────┐
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ░░ ░░ │
│ ░░ > Search Raycast... ░░ │
│ ░░ ░░ │
│ ░░ ┌────────────────────────────────────────────────┐ ░░ │
│ ░░ │ [A] Applications Cmd+1 │ ░░ │
│ ░░ │ [F] File Search Cmd+2 │ ░░ │
│ ░░ │ [C] Clipboard History Cmd+3 │ ░░ │
│ ░░ │ [S] System Commands Cmd+4 │ ░░ │
│ ░░ └────────────────────────────────────────────────┘ ░░ │
│ ░░ ░░ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
└────────────────────────────────────────────────────────────┘
↑ Vibrancyがウィンドウ越しにデスクトップを表示
3. 視覚フィードバック付きのキーボード操作
すべてのアクションにキーボードショートカットがあります。すべてのショートカットに視覚的確認があります。
ショートカット表示パターン:
RESULTS LIST:
┌────────────────────────────────────────────────────────────┐
│ Recent Applications │
│ │
│ (*) Visual Studio Code Cmd+1 │
│ ~/Projects/my-app │
│ │
│ ( ) Figma Cmd+2 │
│ ~/Design/project.fig │
│ │
│ ( ) Terminal Cmd+3 │
│ │
│ ───────────────────────────────────────────────────────────│
│ Actions Cmd+K or -> to see │
└────────────────────────────────────────────────────────────┘
アクションパネル:
┌────────────────────────────────────────────────────────────┐
│ "Visual Studio Code" のアクション esc ← │
│ │
│ 開く Enter │
│ 新規ウィンドウで開く Cmd+Enter │
│ ──────────────────────────────────────────────────────── │
│ Finderで表示 Cmd+Shift+F │
│ パスをコピー Cmd+Shift+C │
│ ──────────────────────────────────────────────────────── │
│ ゴミ箱に移動 Cmd+Backspace │
└────────────────────────────────────────────────────────────┘
デザイン原則: 1. **ショートカットをインラインで表示** - ツールチップではなく、直接表示 - 修飾キーでショートカットをグループ化 - 期待される箇所では標準macOSショートカットを使用 - ニーモニックパターン(⌘⇧F = Finder)
4. 細部に宿る個性
Raycastは効率的でありながら無機質ではない。小さな喜びが記憶に残る体験を生む。
例: - 達成時のコンフェティ演出 - アクションに控えめなサウンド - 遊び心のあるエンプティステート - パワーユーザー向けのイースターエッグ
空の状態の例:
┌────────────────────────────────────────────────────────────┐
│ │
│ [?] │
│ │
│ 「asdfgh」の検索結果はありません │
│ │
│ 別のキーワードで検索するか、 │
│ 拡張機能ストアをご覧ください │
│ │
│ [拡張機能を閲覧] │
│ │
└────────────────────────────────────────────────────────────┘
紙吹雪パターン(達成時):
// オンボーディング完了後、バッジ獲得時など
ConfettiView()
.transition(.opacity)
.animation(.spring(), value: showConfetti)
パーソナリティガイドライン: - 楽しさは遅延を招いてはならない - お祝いは達成で得られる(ランダムではない) - コピーに個性を(“〜の検索結果はありません…” vs “エラー: 0件”) - サウンドは任意で控えめ
5. 拡張機能エコシステムのデザイン
Raycastの拡張機能ストアは美しくデザインされています。
拡張機能カード:
┌────────────────────────────────────────────────────────────┐
│ ┌──────┐ │
│ │ [+] │ GitHub │
│ │ │ Search repos, PRs, and issues │
│ └──────┘ │
│ ★★★★★ 1.2k ratings • By Raycast │
│ │
│ [Install] │
└────────────────────────────────────────────────────────────┘
拡張機能の詳細ビュー:
┌────────────────────────────────────────────────────────────┐
│ │
│ ← GitHub [アンインストール] │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [使用中の拡張機能のスクリーンショット] │ │
│ │ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ コマンド │
│ ├─ リポジトリを検索 │
│ ├─ マイプルリクエスト │
│ ├─ Issue を検索 │
│ └─ Issue を作成 │
│ │
│ 変更履歴 │
│ ├─ v2.1.0 組織フィルタリングを追加 │
│ └─ v2.0.0 新しい API で完全に書き換え │
│ │
└────────────────────────────────────────────────────────────┘
6. 拡張機能のフォームデザイン
拡張機能は一貫したフォームシステムを使用します。
フォームパターン:
┌────────────────────────────────────────────────────────────┐
│ Create GitHub Issue esc │
│ │
│ Repository │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ raycast/extensions ▾ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Title │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Bug: Search not working │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Description │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ When I search for... │ │
│ │ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Labels (optional) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ [bug] [needs-triage] + │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ [Create Issue ⌘↵] [Cancel esc] │
│ │
└────────────────────────────────────────────────────────────┘
フォームコンポーネントのスタイリング:
// SwiftUIでのRaycastスタイルフォーム
Form {
Section("Repository") {
Picker("", selection: $repo) {
ForEach(repos) { repo in
Text(repo.name).tag(repo)
}
}
.labelsHidden()
}
Section("タイトル") {
TextField("バグ: ...", text: $title)
}
Section("Description") {
TextEditor(text: $description)
.frame(minHeight: 100)
}
}
.formStyle(.grouped)
学ぶべきデザインパターン
検索ファーストパターン
すべては検索から始まります。
┌────────────────────────────────────────────────────────────┐
│ > | │
│ ^ 起動時すぐにカーソルがここに │
│ │
│ コマンド、アプリ、ファイルなどを検索 │
└────────────────────────────────────────────────────────────┘
実装: - 起動時に検索へ自動フォーカス - ハイライト付きファジーマッチング - スマートランキング(最近 + 頻繁 + 関連性) - 入力中に結果が表示されます
階層ナビゲーション
複雑さを伴わない深い機能性。
レベル1: メイン検索
↓ Enter
レベル2: 拡張機能コマンド
↓ Enter
レベル3: アクション結果
↓ ⌘K
レベル4: アクションパネル
常に: esc で1つ上の階層に戻る
HUD確認
操作を中断しない素早いフィードバック。
アクション後:
┌──────────────────────────────────┐
│ ✓ クリップボードにコピーしました │
└──────────────────────────────────┘
↑ 短時間表示され、フェードアウト
閉じる操作は不要
// HUDパターン
struct HUDView: View {
@State private var show = false
var body: some View {
if show {
Text("✓ コピーしました")
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(.regularMaterial)
.cornerRadius(8)
.transition(.opacity.combined(with: .scale))
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation { show = false }
}
}
}
}
}
Raycastから盗むべきもの
ランチャー/検索UIに
- 50ms以内が絶対条件 - スピードは妥協できない
- あらゆる場所にキーボードショートカット - 隠さず、発見しやすく
- ハイライト付きファジー検索 - なぜ結果がマッチしたか表示する
- アクションパネル(⌘K) - コンテキストに応じたアクション
- HUD確認 - 素早く、ブロッキングしないフィードバック
macOSアプリに
- ネイティブのバイブランシー - NSVisualEffectView/.materialを使用
- SF Symbols - システムとの一貫性
- システムカラー - アクセントカラー設定を尊重
- 連続的な角丸 - .cornerRadius(style: .continuous)
- 標準ショートカット - ⌘C、⌘Vなどを再発明しない
具体的なテクニック
| テクニック | 適用方法 |
|---|---|
| 即時起動 | ウィンドウを事前にウォームアップ、コンテンツは遅延読み込み |
| ファジー検索 | Fuse.jsなどのライブラリを使用 |
| ショートカットヒント | 結果に⌘1、⌘2などをインラインで表示 |
| アクションパネル | ⌘Kまたは→でセカンダリアクション |
| HUDフィードバック | トースト形式、自動で消える |
| 拡張機能API | 明確な規約、一貫したUI |
カラーシステム
Raycastはシステムに紐づいたセマンティックカラーを使用しています。
// Raycast風セマンティックカラー
extension Color {
static let rayBackground = Color(nsColor: .windowBackgroundColor)
static let raySecondary = Color.secondary
static let rayTertiary = Color(nsColor: .tertiaryLabelColor)
// ステータスカラー
static let raySuccess = Color.green
static let rayWarning = Color.orange
static let rayError = Color.red
// アクセントはシステムに従う
static let rayAccent = Color.accentColor
}
// ダークモードの例
struct ResultRow: View {
var body: some View {
HStack {
Image(systemName: "app.fill")
.foregroundStyle(.secondary)
Text("アプリケーション名")
.foregroundStyle(.primary)
Spacer()
Text("⌘ 1")
.font(.caption)
.foregroundStyle(.tertiary)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(Color.secondary.opacity(0.2))
.cornerRadius(4)
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
}
}
重要な洞察
「ミリ秒単位の差が重要。ユーザーは50msと100msの違いを体感できる。」
「キーボードショートカットは発見可能であるべきで、ドキュメントから暗記するものではない。」
「個性がソフトウェアを記憶に残るものにする。効率性がそれを手放せないものにする。」
「拡張機能はネイティブに感じられるべきで、後付けのように見えてはならない。同じUIの言語、同じインタラクション。」
よくある質問
Raycastはどのようにして50ms未満の応答時間を実現しているのか?
Raycastはネイティブなswift実装(Electronではない)を使用し、起動時に拡張機能のインデックスを事前読み込みし、検索結果を積極的にキャッシュし、レンダリングパイプラインを最適化しています。データが非同期で読み込まれる間もUIは即座に表示されます。50msの閾値は目標ではなく、絶対的な制約として扱われています。
なぜRaycastはmacOSにこれほどネイティブに見えるのか?
RaycastはNSVisualEffectViewを使用してバイブランシー/ブラー効果を実現し、すべてのアイコンにSF Symbolsを使用し、ユーザー設定を尊重するシステムアクセントカラーを採用し、標準的なmacOSキーボードショートカットを使用しています。ウィンドウは連続的な角丸を持ち、システムのダーク/ライトモードに自動的に追従します。
Raycastのキーボードショートカットへのアプローチとは?
すべてのアクションにはキーボードショートカットが結果内にインラインで表示され(⌘1、⌘2など)、ツールチップに隠されることはありません。ショートカットはニーモニックパターンを使用しています(⌘⇧FでFinder、⌘⇧CでCopy Path)。 アクションパネル(⌘K)は独自のショートカットを持つ補助アクションを提供します。
Raycastはスピードを犠牲にせずにどのように個性を加えているのか?
お祝い演出は獲得した瞬間に短く表示されます—紙吹雪は達成後に現れ、ランダムには表示されません。空の状態では遊び心のあるコピーが使われます(「Error: 0 results」ではなく「No results for…」)。サウンドはオプションで控えめです。楽しさがユーザーをブロックしたり、レイテンシを増加させることは決してありません。
Raycastの拡張機能システムはどのように一貫性を維持しているのか?
すべての拡張機能は同じUIコンポーネント(フォーム、リスト、アクションパネル)を使用し、同じナビゲーションパターン(Enterで詳細へ、Escapeで戻る)に従い、システムスタイリングを継承します。APIが一貫性を強制するため、サードパーティの拡張機能も組み込み機能と見分けがつかないほど自然に感じられます。
リソース
- ウェブサイト: raycast.com
- ストア: raycast.com/store - 拡張機能のデザインを研究
- ドキュメント: 拡張機能開発ガイドライン
- ブログ: パフォーマンスに関するエンジニアリング記事
- GitHub: github.com/raycast - オープンソース拡張機能