ウィンドウの影を付ける・消す

shadow 設定と setShadow()(権限 core:window:allow-set-shadow)で影を切り替える。Windows で枠付きの影が消えない、フレームレスで 1px の白い枠線が出る問題の対処も示す。

ウィンドウ 対象: Tauri 2.x 更新日: 読了目安: 約7分 win-014
目次
  1. 前提条件
  2. 設定と実際の見た目
  3. 1. フロントエンドから実装する (TypeScript)
  4. 実行中に影を切り替える
  5. 枠を消して影を付け直す
  6. 白い線を避けて、影を CSS で描く
  7. 2. バックエンドから実装する (Rust)
  8. 動作確認
  9. よくあるエラーと対処法
  10. OS ごとの違いと注意点
  11. 関連レシピ

ウィンドウの周りの影は、重なったウィンドウの境目を分かりやすくする一方、デスクトップになじませたいウィジェットや、形を自由にした透明ウィンドウでは邪魔になります。影は設定ファイルの shadow(既定は true)で起動時に決め、実行中は JS の setShadow() か Rust の set_shadow() で切り替えます。やっかいなのは、同じ設定でも OS と枠(decorations)の有無で結果が変わることです。フレームレスや透明なウィンドウで影をどう扱うかは、このレシピにまとめます。

前提条件

プラグインは不要です。setShadow() の権限 core:window:allow-set-shadow は core:window:default に含まれないので追加します。後述の「枠を消して影を付け直す」例では core:window:allow-set-decorations も使います。起動時の設定だけで済むなら権限は要りません。

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "core:window:allow-set-shadow",
    "core:window:allow-set-decorations"
  ]
}

影付きのフレームレスウィンドウにする設定です。shadow は省略しても true ですが、意図を残すために書いておくと後で迷いません。

{
  "app": {
    "windows": [
      { "label": "main", "width": 800, "height": 600, "decorations": false, "shadow": true }
    ]
  }
}

設定と実際の見た目

shadow の効き方は次のとおりです。macOS は設定どおりに付き外しされますが、Windows は枠の有無で結果が変わり、Linux では設定が効きません。

枠shadowWindowsmacOSLinux
ありtrue影あり影あり非対応
ありfalse影は消えない影なし非対応
なしtrue影あり + 1px の白い枠線(Windows 11 は角丸)影あり非対応
なしfalse影なし影なし非対応

つまり Windows では、影を消したければ枠も消すしかなく、枠を消して影を付けると細い白い線が付きます。反対に、Windows 11 でフレームレスの角を丸くしたいときは shadow: true が近道です。

1. フロントエンドから実装する (TypeScript)

実行中に影を切り替える

影の有無を読み取る API は無いので、今の状態はアプリ側で覚えておきます。

import { getCurrentWindow } from '@tauri-apps/api/window';

const win = getCurrentWindow();
let shadowOn = true; // 設定ファイルの shadow と合わせる

// ミニ表示では影を消してデスクトップになじませる
export async function setMiniMode(mini: boolean) {
  if (shadowOn === !mini) return;
  await win.setShadow(!mini); // core:window:allow-set-shadow が必要
  shadowOn = !mini;
}

枠を消して影を付け直す

通常の枠で起動したウィンドウを、実行中に「影付きのフレームレス」に切り替えるときは、枠と影を続けて指定します。

import { getCurrentWindow } from '@tauri-apps/api/window';

const win = getCurrentWindow();

export async function applyFramelessLook() {
  await win.setDecorations(false); // core:window:allow-set-decorations
  await win.setShadow(true);       // Windows 11 では角も丸くなる
}

枠を消すと移動や閉じるボタンも無くなるので、自作のタイトルバーと組み合わせます(フレームレスウィンドウ、自作のタイトルバーにメニューを組み込む)。

白い線を避けて、影を CSS で描く

Windows の白い線をどうしても避けたい場合や、Linux でも同じ見た目にしたい場合は、shadow: false にした透明なウィンドウの中で、影をページ側に描きます。影がはみ出す分だけ内側に余白を取るのがポイントです。

:root,
body {
  background: transparent;
  margin: 0;
}

/* ウィンドウの端から 12px 内側に本体を置き、余白に影を落とす */
.frame {
  position: fixed;
  inset: 12px;
  border-radius: 10px;
  background: #fff;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

設定ファイルでは "transparent": true、"decorations": false、"shadow": false を指定します。余白も見えないだけでウィンドウの一部なので、そこをクリックしても背後のアプリに届くとは限りません。気になるなら クリックスルー と組み合わせます。

2. バックエンドから実装する (Rust)

新しく作るウィンドウはビルダーの shadow()、作成済みのウィンドウは set_shadow() で指定します。ウィンドウを作るコマンドは、Windows で固まらないよう async にします。

use tauri::{WebviewUrl, WebviewWindowBuilder};

/// 呼び出し元ウィンドウの影を切り替える
#[tauri::command]
fn set_window_shadow(window: tauri::WebviewWindow, enable: bool) -> Result<(), String> {
    window.set_shadow(enable).map_err(|e| e.to_string())
}

/// 呼び出し元ウィンドウを「枠なし + 影あり」にする
#[tauri::command]
fn apply_frameless_look(window: tauri::WebviewWindow) -> Result<(), String> {
    window.set_decorations(false).map_err(|e| e.to_string())?;
    window.set_shadow(true).map_err(|e| e.to_string())
}

/// 影付きのフレームレスウィンドウを新しく開く
#[tauri::command]
async fn open_palette(app: tauri::AppHandle) -> Result<(), String> {
    WebviewWindowBuilder::new(&app, "palette", WebviewUrl::App("palette.html".into()))
        .inner_size(360.0, 480.0)
        .decorations(false)
        .shadow(true)
        .build()
        .map_err(|e| e.to_string())?;
    Ok(())
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            set_window_shadow,
            apply_frameless_look,
            open_palette
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Rust のコマンド経由なら、JS 側に core:window:allow-set-shadow などの権限は要りません。

import { invoke } from '@tauri-apps/api/core';

await invoke('set_window_shadow', { enable: false });
await invoke('apply_frameless_look');
await invoke('open_palette');

動作確認

npm run tauri dev で起動し、setMiniMode(true) と setMiniMode(false) を交互に呼んで見た目を比べます。Windows では、枠付きのままだと何も変わらないのが正常です。applyFramelessLook() の後に切り替えると、影と白い線がまとめて付いたり消えたりします。Windows 11 では影を付けると角も丸くなります。

よくあるエラーと対処法

  • 「window.set_shadow not allowed. Permissions associated with this command: core:window:allow-set-shadow」: 権限の追加漏れです。リリースビルドでは「Command plugin:window|set_shadow not allowed by ACL」と出ます。
  • Windows で setShadow(false) しても影が消えない: 枠付きのウィンドウです。Windows で影を消すには decorations: false にします。
  • フレームレスの周りに白い線が出る: Windows で枠なしのまま shadow: true になっています。線が不要なら shadow: false にし、必要なら CSS で影を描きます。
  • Linux で見た目が変わらない: shadow と setShadow() は Linux では非対応で、指定しても見た目は変わりません。

OS ごとの違いと注意点

  • Windows: 枠付きのウィンドウは常に影があり、false は効きません。枠なしで true にすると 1px の白い枠線が付き、Windows 11 では角が丸くなります。透明なウィンドウでもこの線は出るので、透明なウィンドウ では shadow: false を基本にしています。
  • macOS: Windows のような例外は無く、枠の有無にかかわらず shadow の値どおりになります。CSS で影を描く方法を使うなら、透明なウィンドウのための macOSPrivateApi が別途必要です。
  • Linux: 非対応です。どの OS でも同じ見た目にしたいなら、CSS で影を描く方法を使います。

関連レシピ

参考リンク(公式ドキュメント)

Web Ninja

この記事を書いた人

Web Ninja ウェブエンジニア (Web Engineer)

会社員ネットワークエンジニアから独立してかれこれ 25 年以上 Web エンジニアとして活動中。普段は JavaScript と Node.js を自在に操り、時には C++ や Perl といった古流の技も嗜みます。近年は Tauri × Rust という新たな武器を手に、デスクトップアプリ開発の最前線を駆け抜けています。「作りたい」を「作れる」に変えるための、実践的な「技」をお届けします。

お問い合わせ: tauri.ninja@gmail.com

内容の誤り・動かないコードを報告する