datalens-html-pages について
制限付きサンドボックス内で DataLens が配信する、独立した HTML レポートやページを作成するスキルです。リソースやセキュリティ、操作上の制約を説明し、アップロード前の検証スクリプトも備えています。
主な機能
- 01独立した HTML ページの作成
- 02サンドボックスと CSP の制約への対応
- 03許可リスト内の外部リソースの利用
- 04ホストへのメッセージによる出力とリンク処理
- 05Python スクリプトによるページ検証
- 06テーマ、言語、モバイルへの対応
活用シーン
- 01DataLens のレポートページ作成
- 02アップロード前の HTML 検査
- 03Yandex Maps JS API 2.1 による地図追加
- 04モバイル表示向けページの準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ datalens-tech/ datalens-skills --skill datalens-html-pagesインストール後、Claude Code で/datalens-html-pagesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/dl-dashboard.cssdl-theme-tokens.cssreport.template.htmlreferences/authoring-constraints.mdcsp-and-sandbox.mddesign-principles.mdpublication.mdscripts/validate_page.py詳細
DataLens の HTML ページ
DataLens は、スタンドアロンの HTML ページ(通常は AI 生成のレポートなどの完全なドキュメント)をプライベートバケットに保存し、制限されたサンドボックス iframe で、短い TTL の署名付き URL からレンダリングします。アップロード時には Content-Security-Policy が <meta> タグとして挿入されます。完全な <!DOCTYPE html> ページを作成できます。スクリプトは実行できますが、許可リストにある CDN からのものに限られ、ほとんどのブラウザー API はブロックされます。
このスキルの対象外: グラフ内の HTML(マークアップ関数、HTML マークアップのテーブルセル、
ChartEditor.generateHtml)。これは別の機能であり、許可されるタグと属性が厳しく制限され、 スクリプトは一切使えません。このスキルの対象はスタンドアロンの HTML ページのみです。
ランタイムの全体像
- サンドボックス iframe:
sandbox="allow-scripts"、allow=""、referrerpolicy="no-referrer"を設定し、 重要な点としてallow-same-originは指定しません。ページは不透明なオリジンで実行されます(親ページもCross-Origin-Opener-Policy: same-originを送信します)。同一オリジン分離はこの設定に依存するため、同一オリジンアクセスを 前提にしないでください。 - 挿入される CSP(自分で記述する必要はありません。プラットフォームが冪等に先頭へ追加します):
default-src 'none'を基本とし、script/style/font/img/mediaに小規模な許可リストを設定します。connect-srcは Yandex Maps のホストのみを許可し、form-action、frame-src、object-src、worker-src、base-uriはすべて'none'です。 ポリシー全文はreferences/csp-and-sandbox.mdを参照してください。 - サーバー側で権限を確認した後、有効期限が 10~30 秒 の署名付き GET で配信されます。テーマと言語は署名付きクエリパラメーターで渡されます。
つまり、ページを完全に自己完結させてください。CSS/JS はインラインで記述し(CSP は
'unsafe-inline'/'unsafe-eval' を許可します)、ライブラリは許可リストにある CDN からのみ読み込み、
ネットワーク、ストレージ、親ページには依存しないでください。
作成ルール
使用可能な外部リソース(それ以外はすべて CSP によってブロックされます):
| リソース | 使用可能な配信元 |
|---|---|
| スクリプト | cdn.jsdelivr.net、cdnjs.cloudflare.com、cdn.tailwindcss.com、yastatic.net、Yandex Maps(api-maps.yandex.ru、*.api-maps.yandex.ru、*.maps.yandex.net、suggest-maps.yandex.ru)(+インライン) |
| スタイル | 上記の CDN + fonts.googleapis.com、blob:(+インライン) |
| フォント | cdn.jsdelivr.net、cdnjs.cloudflare.com、fonts.gstatic.com、data: |
| 画像 | yastatic.net、Yandex Maps のホスト、data:、blob: |
| メディア | data:、blob: のみ |
ネットワーク(connect-src) | Yandex Maps のホストのみ(Maps API 自体のためのものであり、自分のデータ用ではありません) |
Google Fonts は標準の方法で読み込みます(fonts.googleapis.com の CSS を <link> で読み込み、
フォントファイルは fonts.gstatic.com から読み込みます)。許可リストにないライブラリ(unpkg、plot.ly、
d3js.org、jQuery)は jsdelivr/cdnjs 経由で配信してください。
Yandex Maps JS API 2.1 はそのまま利用できます。api-maps.yandex.ru から読み込み(ユーザーが API キーを持っている場合はそれを使用)、固定の高さを設定したコンテナー内に描画してください。スクリプト、タイル、API 自体のリクエストは許可されています。v3 ではなく 2.1 を使用してください。v3 はキーがないと読み込みを拒否し、サンドボックスでブロックされるワーカーを必要とします。サンドボックスのその他の制約はすべて適用されます。自分のデータは取得せず、インラインで埋め込んでください。
<script src="https://api-maps.yandex.ru/2.1/?apikey=YOUR_KEY&lang=ru_RU"></script>
<div id="map" style="height: 400px"></div>
<script>ymaps.ready(() => new ymaps.Map('map', {center: [55.76, 37.64], zoom: 10}));</script>
ブロックされるため、使用しないでください(エラーになるか、何も起こらず失敗します):
- ストレージ:
localStorage、sessionStorage、indexedDB、document.cookie、Cache API → すべての状態はメモリ内に保持してください。 - ネットワーク: Yandex Maps のホスト以外への
fetch、XMLHttpRequest、WebSocket、EventSource、sendBeacon。connect-srcはそれ以外を一切許可しません。データを取得するのではなく、ページにインラインで埋め込んでください。 - フレーム/オブジェクト/フォーム: 入れ子の
<iframe>、<object>、<embed>、<form>の送信、<base>。 - ワーカー、ポップアップ、ダイアログ、ダウンロード、カメラ/位置情報/全画面表示、親フレームの移動、通常のリンク遷移。
<a href>は単独では開きません。クリックはOPEN_URL経由で処理してください(下記のリンクを開くを参照)。
テーマと言語 — クエリ文字列から取得してください:
const theme = new URLSearchParams(location.search).get('theme'); // 'light' | 'dark' | 'system'
const lang = new URLSearchParams(location.search).get('lang'); // 'ru' | 'en'
HTML を設計・生成する際は、利用可能なテーマと言語に対応し、レスポンシブにもなるページを作成してください。DataLens のモバイル Web 版で表示するユーザーもいます。
ファイルのエクスポート — ダウンロード API はブロックされるため、データはホストに渡してください。ホスト側で event.source を検証し、MIME タイプの許可リストとサイズ上限を適用して、ファイル名をサニタイズしてからダウンロードを作成します。
parent.postMessage({ code: 'EXPORT', data: { name: 'report.csv', mime: 'text/csv', data: csvString } }, '*');
リンクを開く — 通常の <a href> ではサンドボックス内を移動できません(不透明なオリジンのため、target="_blank" でも about:blank にしか移動しません)。URL を開くには、ホストに要求します。クリックをインターセプトして preventDefault() を呼び出し、OPEN_URL をポストします。委譲リスナーを 1 つ設定すれば、すべてのリンクに対応できます。ただし、フレーム内にある場合に限定してください。そうすることで、トップレベルで開いた場合やローカルでプレビューした場合にもファイルが動作します。
if (window.parent !== window) {
document.addEventListener('click', (e) => {
const a = e.target.closest('a[href]');
if (!a) return;
const href = a.getAttribute('href');
if (!href || href.startsWith('#')) return; // in-page anchors scroll normally — leave them
e.preventDefault();
parent.postMessage({ code: 'OPEN_URL', data: { url: a.href } }, '*');
});
}
エンコーディングとサイズ(アップロード時に適用):
- 有効な UTF-8 を使用してください。
<meta charset="utf-8">は 先頭 1024 バイト以内に記述してください。サーバーは文字化けを検出しますが、修復はしません。 - ドキュメントを Markdown のコードフェンスで囲まないでください(
```html)。<head>が早期に閉じられてしまいます。 - ページサイズは 約 10 MB までです。できれば 約 5 MB 未満にしてください。
網羅的な一覧は references/authoring-constraints.md を、要件に準拠した出発点としては assets/report.template.html を参照してください。
デザイン — ダッシュボードやレポートページを作成する前に、DataLens のデザイン原則に沿ったスタイルを希望するか、ユーザーに尋ねてください(すでに希望が示されている場合を除きます)。尋ねられない場合(回答を得る方法がない非対話型の実行など)は、デフォルトで適用しないでください。ページはシンプルなスタイルにし、出力でデザイン原則を適用しなかったことを伝えてください。同意が得られた場合は、references/design-principles.md に従ってください。そこでは、レイアウトグリッド、チャートタイプの選択、色、数値の書式設定、テーマ設定について説明しています。この原則は、assets/dl-theme-tokens.css の DataLens デザイントークンと、assets/dl-dashboard.css のダッシュボード基本スタイルを使用します。ページを自己完結させるため、両ファイルの内容を <style> にインラインで記述し、theme クエリパラメーターに応じてルート要素に g-root g-root_theme_light|dark を設定してください。ユーザーが希望しない場合は、これら 3 つのファイルを使わず、希望に合わせてページのスタイルを設定してください。上記のサンドボックスと CSP のルールは引き続き適用されます。
アップロード前の検証
ページを CSP の正確な許可リストとサンドボックスの制約に照らしてリントしてください。検証に合格すれば、レンダリング時にブロックされたり削除されたりするものはありません。
python scripts/validate_page.py report.html # errors + warnings
python scripts/validate_page.py --strict report.html # warnings fail too (CI mode)
cat report.html | python scripts/validate_page.py - # from stdin
許可リストにないリソースホスト、ブロック対象の API(storage/fetch/workers など)、ブロック対象のタグ、charset の欠落、文字化け、コードフェンスによる囲み、ページサイズ超過を検出します。python scripts/validate_page.py --self-test を実行して、リンター自体を確認してください。
配信と公開
ページをアップロードまたは公開し、恒久的なリンクを返す方法については、references/publication.md を参照してください。
配信はサーバー側で行われます。正しくページを作成できるよう、次の仕様を把握してください。
- 署名付き GET の有効期間は 10~30 秒で、権限チェック後にのみ発行されます(TOCTOU の隙間はありません)。長期間有効な URL を前提とするフローは作成しないでください。
- オブジェクトは
Content-Type: text/html; charset=utf-8、Content-Disposition: inline、Cache-Control: no-storeを付けて保存されます。アップロードはサーバー側の JSON-RPC で行われます(署名付き PUT はありません。CSP の挿入とメタデータの管理はサーバーが行います)。 - iframe には上記のサンドボックスが適用され、親は
COOP: same-originを送信します。
設計上考慮すべき許容済みのリスク(詳細は references/csp-and-sandbox.md を参照):URL をトップレベルで開くと(有効期間内)、挿入された CSP は維持されますが、iframe のサンドボックスは適用されません。ページは <meta http-equiv="refresh"> を使って自ら移動できます。また、許可リストにある CDN は SRI による固定がされていません。フレームは同一オリジンから隔離され、ネットワークにも接続されないため、非公開にしておく必要がある閲覧データ、パラメーター、またはリアルタイムの LLM 出力をページに含めないでください。これは信頼できるアプリケーションの画面ではなく、レポートです。
よくある落とし穴 → 解決策
| 症状 | 原因 | 解決策 |
|---|---|---|
| ライブラリが読み込まれない/コンソールに CSP エラーが表示される | スクリプト/スタイルのホストが許可リストにない | jsdelivr / cdnjs / tailwind / yastatic から配信する |
| データが表示されない | コードが fetch/XHR を呼び出している | 生成時にデータセットをページにインライン化する |
| 「localStorage is not available」と表示される/例外が発生する | サンドボックス内でストレージ API を使用している | 状態をメモリ内に保持する |
| フォントが表示されない | フォントのホストが許可リストにない | Google Fonts(fonts.googleapis.com の CSS と fonts.gstatic.com のファイル)または jsdelivr/cdnjs を使用する |
| 画像が表示されない | http:// または許可リストにないホストを使用している | yastatic.net、data:、または blob: を使用する |
| Yandex マップが表示されない | Maps v3 を使用している、API がミラーから読み込まれている、またはコンテナーに高さが設定されていない | api-maps.yandex.ru の JS API 2.1 を使用し、コンテナーに明示的な高さを指定する |
| ダウンロードボタンを押しても何も起きない | ダウンロード API がブロックされている | {code:'EXPORT', data:{name,mime,data}} を親にポストする |
| リンクをクリックしても何も起きない | サンドボックス内ではリンクが遷移しない | クリックを捕捉し、{code:'OPEN_URL', data:{url}} をポストする |
| ページがプレーンテキストで表示される/head が壊れている | Markdown のコードフェンスで囲んでいる | ``` フェンスを削除する |
| アップロードが拒否される | 10 MB 超または UTF-8 が無効 | アセットを縮小し、UTF-8 と早い位置の <meta charset> を確認する |