RGBずれグリッチ
ホバー/マウス移動で R・G・B チャンネルが左右にずれるアナグリフ風グリッチ。mix-blend-mode:screen で重ねた3レイヤーをマウス速度に応じて分離し、サイバー/音楽系のアクセントに最適です。
ライブデモ
使用例(お題: SaaS FlowDesk)
この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- FlowDesk:新機能ローンチ告知(RGBずれグリッチ) -->
<!-- 各チャンネル抽出用の SVG カラーマトリクス(画面には出ない) -->
<svg class="fd-rgb__defs" width="0" height="0" aria-hidden="true" focusable="false">
<defs>
<filter id="fdOnlyR" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
</filter>
<filter id="fdOnlyG" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
</filter>
<filter id="fdOnlyB" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
</svg>
<section class="fd-rgb-wrap">
<!-- 製品画像を3枚重ねて RGB をずらす -->
<figure class="fd-rgb" tabindex="0" aria-label="RGBずれグリッチ画像">
<img class="fd-rgb__layer fd-rgb__layer--r" src="https://picsum.photos/seed/flowdesk-launch/720/520" alt="" aria-hidden="true">
<img class="fd-rgb__layer fd-rgb__layer--g" src="https://picsum.photos/seed/flowdesk-launch/720/520" alt="" aria-hidden="true">
<img class="fd-rgb__layer fd-rgb__layer--b" src="https://picsum.photos/seed/flowdesk-launch/720/520" alt="新機能のプレビュー">
<figcaption class="fd-rgb__cap">v2.0</figcaption>
</figure>
<!-- 告知テキスト -->
<div class="fd-rgb__info">
<span class="fd-rgb__tag">MAJOR UPDATE</span>
<h2 class="fd-rgb__title">FlowDesk 2.0、<br>始動。</h2>
<p class="fd-rgb__lead">AIワークフローと、刷新したダッシュボード。チームのスピードを次の次元へ。</p>
<a class="fd-rgb__btn" href="#">新機能を見る →</a>
</div>
</section>
CSS
/* FlowDesk:新機能ローンチ告知(RGBずれグリッチ) */
:root {
--navy: #0f1b34;
--blue: #4f7cff;
--shift: 0px; /* JSが渡すずれ量(px) */
--skew: 0deg; /* 速度に応じた歪み */
--scan: 0; /* 走査線の濃さ */
}
* { box-sizing: border-box; }
body {
margin: 0;
height: 400px;
display: flex;
align-items: center;
gap: 30px;
padding: 0 32px;
font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
background: radial-gradient(120% 120% at 100% 0%, #1a2a52 0%, var(--navy) 70%);
color: #fff;
overflow: hidden;
}
/* SVGフィルタ定義はレイアウトに影響させない */
.fd-rgb__defs { position: absolute; width: 0; height: 0; pointer-events: none; }
/* グリッチ枠 */
.fd-rgb {
position: relative;
flex: 0 0 360px;
height: 300px;
margin: 0;
border-radius: 14px;
overflow: hidden;
cursor: crosshair;
outline: none;
background: #000;
box-shadow: 0 18px 45px rgba(0,0,0,0.55);
}
.fd-rgb:focus-visible { box-shadow: 0 0 0 3px var(--blue), 0 18px 45px rgba(0,0,0,0.55); }
/* 3レイヤーを screen 合成(重なれば元の色) */
.fd-rgb__layer {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
mix-blend-mode: screen;
will-change: transform;
}
.fd-rgb__layer--r { filter: url(#fdOnlyR); }
.fd-rgb__layer--g { filter: url(#fdOnlyG); }
.fd-rgb__layer--b { filter: url(#fdOnlyB); }
/* R は左、B は右へ。G は基準 */
.fd-rgb__layer--r { transform: translateX(calc(var(--shift) * -1)) skewX(var(--skew)); }
.fd-rgb__layer--g { transform: translateX(0); }
.fd-rgb__layer--b { transform: translateX(var(--shift)) skewX(calc(var(--skew) * -1)); }
/* 走査線 */
.fd-rgb::after {
content: "";
position: absolute;
inset: 0;
background: repeating-linear-gradient(
to bottom,
rgba(0,0,0,0) 0,
rgba(0,0,0,0.14) 2px,
rgba(0,0,0,0) 3px
);
pointer-events: none;
opacity: var(--scan, 0);
transition: opacity 0.25s ease;
}
.fd-rgb__cap {
position: absolute;
left: 14px;
bottom: 12px;
z-index: 3;
font-size: 16px;
font-weight: 800;
letter-spacing: 0.2em;
color: #fff;
text-shadow: 2px 0 #ff003c, -2px 0 #00e5ff;
pointer-events: none;
}
/* 告知テキスト */
.fd-rgb__info { flex: 1; }
.fd-rgb__tag { font-size: 10px; letter-spacing: 0.3em; color: #8aa6ff; }
.fd-rgb__title {
margin: 10px 0 12px;
font-size: 27px;
line-height: 1.35;
font-weight: 800;
}
.fd-rgb__lead {
margin: 0 0 22px;
font-size: 13px;
line-height: 1.85;
max-width: 280px;
color: rgba(255,255,255,0.75);
}
.fd-rgb__btn {
display: inline-block;
padding: 11px 22px;
border-radius: 10px;
background: var(--blue);
color: #fff;
font-size: 13px;
font-weight: 700;
text-decoration: none;
box-shadow: 0 10px 24px rgba(79,124,255,0.45);
transition: transform 0.2s ease;
}
.fd-rgb__btn:hover { transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
.fd-rgb__layer { transition: none; }
.fd-rgb__btn { transition: none; }
}
JavaScript
// マウスの移動量(速度)に応じて R/G/B のずれ量を CSS 変数で増減する
(() => {
const rgb = document.querySelector(".fd-rgb");
if (!rgb) return;
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
const MAX = 14; // 最大ずれ量(px)
const HOVER_BASE = 4; // ホバー中の最低ずれ量(px)
let shift = 0;
let target = 0;
let lastX = 0, lastY = 0, hasLast = false;
let active = false;
let raf = 0;
// 速度→ずれ量
const onMove = (e) => {
active = true;
if (hasLast) {
const dx = e.clientX - lastX;
const dy = e.clientY - lastY;
const speed = Math.min(Math.hypot(dx, dy), 60);
target = Math.min(HOVER_BASE + speed * 0.45, MAX);
} else {
target = HOVER_BASE;
}
lastX = e.clientX;
lastY = e.clientY;
hasLast = true;
start();
};
const onEnter = () => { active = true; target = HOVER_BASE; start(); };
const onLeave = () => { active = false; target = 0; hasLast = false; };
const onFocus = () => { active = true; target = HOVER_BASE + 4; start(); };
const onBlur = () => { active = false; target = 0; };
rgb.addEventListener("pointermove", onMove);
rgb.addEventListener("pointerenter", onEnter);
rgb.addEventListener("pointerleave", onLeave);
rgb.addEventListener("focus", onFocus);
rgb.addEventListener("blur", onBlur);
const tick = () => {
if (active && target > HOVER_BASE) target += (HOVER_BASE - target) * 0.06;
shift += (target - shift) * 0.2;
const skew = (shift / MAX) * 2.5;
rgb.style.setProperty("--shift", `${shift.toFixed(2)}px`);
rgb.style.setProperty("--skew", `${skew.toFixed(2)}deg`);
rgb.style.setProperty("--scan", `${Math.min(shift / 6, 1).toFixed(2)}`);
if (!active && shift < 0.05) {
shift = 0;
rgb.style.setProperty("--shift", "0px");
rgb.style.setProperty("--skew", "0deg");
rgb.style.setProperty("--scan", "0");
raf = 0;
return;
}
raf = requestAnimationFrame(tick);
};
const start = () => { if (!raf && !reduce) raf = requestAnimationFrame(tick); };
// reduce 指定時はホバーで固定ずれのみ
if (reduce) {
rgb.addEventListener("pointerenter", () => {
rgb.style.setProperty("--shift", `${HOVER_BASE}px`);
rgb.style.setProperty("--scan", "0.5");
});
rgb.addEventListener("pointerleave", () => {
rgb.style.setProperty("--shift", "0px");
rgb.style.setProperty("--scan", "0");
});
}
})();
コード
HTML
<!-- ホバー/マウス移動で R/G/B チャンネルが左右にずれるアナグリフ風グリッチ -->
<!-- 各チャンネル抽出用の SVG カラーマトリクス(画面には出ない) -->
<svg class="rgb__defs" width="0" height="0" aria-hidden="true" focusable="false">
<defs>
<!-- R のみ残す -->
<filter id="only-r" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
</filter>
<!-- G のみ残す -->
<filter id="only-g" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
</filter>
<!-- B のみ残す -->
<filter id="only-b" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
</svg>
<div class="stage">
<figure class="rgb" tabindex="0" aria-label="RGBずれグリッチ画像">
<!-- 同一画像を3枚重ね、CSSで R/G/B に着色し transform でずらす -->
<img class="rgb__layer rgb__layer--r" src="https://picsum.photos/seed/rgbshift/800/600" alt="" aria-hidden="true">
<img class="rgb__layer rgb__layer--g" src="https://picsum.photos/seed/rgbshift/800/600" alt="" aria-hidden="true">
<img class="rgb__layer rgb__layer--b" src="https://picsum.photos/seed/rgbshift/800/600" alt="RGBずれグリッチ対象画像">
<figcaption class="rgb__cap">R G B</figcaption>
</figure>
</div>
CSS
:root {
--shift: 0px; /* JSが渡すずれ量(px) */
--skew: 0deg; /* 速度に応じた歪み */
--radius: 12px;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background:
radial-gradient(120% 120% at 50% 10%, #15131f 0%, #07060b 70%);
font-family: "Courier New", ui-monospace, monospace;
}
.stage { padding: 24px; }
/* SVGフィルタ定義はレイアウトに影響させない */
.rgb__defs { position: absolute; width: 0; height: 0; pointer-events: none; }
.rgb {
position: relative;
width: min(68vw, 400px);
aspect-ratio: 4 / 3;
margin: 0;
border-radius: var(--radius);
overflow: hidden;
cursor: crosshair;
outline: none;
background: #000;
box-shadow: 0 20px 50px -18px rgba(0, 0, 0, .9);
}
.rgb:focus-visible { box-shadow: 0 0 0 3px #00e5ff, 0 20px 50px -18px rgba(0, 0, 0, .9); }
/* 3レイヤーを重ね、screen 合成で加法混色(重なれば元の色に戻る) */
.rgb__layer {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
mix-blend-mode: screen;
will-change: transform;
}
/* 各チャンネルだけ残す(他2色を落とす)色行列フィルタ */
.rgb__layer--r { filter: url(#only-r); }
.rgb__layer--g { filter: url(#only-g); }
.rgb__layer--b { filter: url(#only-b); }
/* R は左、B は右へ。G は基準。--shift をJSで増減 */
.rgb__layer--r { transform: translateX(calc(var(--shift) * -1)) skewX(var(--skew)); }
.rgb__layer--g { transform: translateX(0); }
.rgb__layer--b { transform: translateX(var(--shift)) skewX(calc(var(--skew) * -1)); }
/* 走査線。ずれが大きいほど濃く(--scan をJSで) */
.rgb::after {
content: "";
position: absolute;
inset: 0;
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, .14) 2px,
rgba(0, 0, 0, 0) 3px
);
pointer-events: none;
opacity: var(--scan, 0);
transition: opacity .25s ease;
}
.rgb__cap {
position: absolute;
left: 14px;
bottom: 12px;
z-index: 3;
font-size: 16px;
font-weight: 700;
letter-spacing: .5em;
color: #fff;
text-shadow: 2px 0 #ff003c, -2px 0 #00e5ff;
pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
.rgb__layer { transition: none; }
}
JavaScript
// マウスの移動量(速度)に応じて R/G/B のずれ量を CSS 変数で増減する
(() => {
const rgb = document.querySelector(".rgb");
if (!rgb) return;
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
const MAX = 14; // 最大ずれ量(px)
const HOVER_BASE = 4; // ホバー中の最低ずれ量(px)
let shift = 0; // 現在のずれ量
let target = 0; // 目標ずれ量
let lastX = 0, lastY = 0, hasLast = false;
let active = false;
let raf = 0;
// 速度→ずれ量。動かすほど大きく、止めれば HOVER_BASE へ戻る
const onMove = (e) => {
active = true;
if (hasLast) {
const dx = e.clientX - lastX;
const dy = e.clientY - lastY;
const speed = Math.min(Math.hypot(dx, dy), 60);
target = Math.min(HOVER_BASE + speed * 0.45, MAX);
} else {
target = HOVER_BASE;
}
lastX = e.clientX;
lastY = e.clientY;
hasLast = true;
start();
};
const onEnter = () => { active = true; target = HOVER_BASE; start(); };
const onLeave = () => { active = false; target = 0; hasLast = false; };
const onFocus = () => { active = true; target = HOVER_BASE + 4; start(); };
const onBlur = () => { active = false; target = 0; };
rgb.addEventListener("pointermove", onMove);
rgb.addEventListener("pointerenter", onEnter);
rgb.addEventListener("pointerleave", onLeave);
rgb.addEventListener("focus", onFocus);
rgb.addEventListener("blur", onBlur);
// ホバー中はじわじわ減衰(止めても完全には消えず微振動)
const tick = () => {
// ホバー継続中は target を少しずつ HOVER_BASE に戻す
if (active && target > HOVER_BASE) target += (HOVER_BASE - target) * 0.06;
shift += (target - shift) * 0.2;
const skew = (shift / MAX) * 2.5; // ずれに比例した skew(deg)
rgb.style.setProperty("--shift", `${shift.toFixed(2)}px`);
rgb.style.setProperty("--skew", `${skew.toFixed(2)}deg`);
rgb.style.setProperty("--scan", `${Math.min(shift / 6, 1).toFixed(2)}`);
// ほぼ静止したらループ停止(非アクティブ時のみ)
if (!active && shift < 0.05) {
shift = 0;
rgb.style.setProperty("--shift", "0px");
rgb.style.setProperty("--skew", "0deg");
rgb.style.setProperty("--scan", "0");
raf = 0;
return;
}
raf = requestAnimationFrame(tick);
};
const start = () => { if (!raf && !reduce) raf = requestAnimationFrame(tick); };
// reduce 指定時はホバーで固定ずれのみ(アニメ無し)
if (reduce) {
rgb.addEventListener("pointerenter", () => {
rgb.style.setProperty("--shift", `${HOVER_BASE}px`);
rgb.style.setProperty("--scan", "0.5");
});
rgb.addEventListener("pointerleave", () => {
rgb.style.setProperty("--shift", "0px");
rgb.style.setProperty("--scan", "0");
});
}
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「RGBずれグリッチ」の効果を追加してください。
# 追加してほしい効果
RGBずれグリッチ(画像エフェクト)
ホバー/マウス移動で R・G・B チャンネルが左右にずれるアナグリフ風グリッチ。mix-blend-mode:screen で重ねた3レイヤーをマウス速度に応じて分離し、サイバー/音楽系のアクセントに最適です。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- ホバー/マウス移動で R/G/B チャンネルが左右にずれるアナグリフ風グリッチ -->
<!-- 各チャンネル抽出用の SVG カラーマトリクス(画面には出ない) -->
<svg class="rgb__defs" width="0" height="0" aria-hidden="true" focusable="false">
<defs>
<!-- R のみ残す -->
<filter id="only-r" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
</filter>
<!-- G のみ残す -->
<filter id="only-g" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
</filter>
<!-- B のみ残す -->
<filter id="only-b" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
</svg>
<div class="stage">
<figure class="rgb" tabindex="0" aria-label="RGBずれグリッチ画像">
<!-- 同一画像を3枚重ね、CSSで R/G/B に着色し transform でずらす -->
<img class="rgb__layer rgb__layer--r" src="https://picsum.photos/seed/rgbshift/800/600" alt="" aria-hidden="true">
<img class="rgb__layer rgb__layer--g" src="https://picsum.photos/seed/rgbshift/800/600" alt="" aria-hidden="true">
<img class="rgb__layer rgb__layer--b" src="https://picsum.photos/seed/rgbshift/800/600" alt="RGBずれグリッチ対象画像">
<figcaption class="rgb__cap">R G B</figcaption>
</figure>
</div>
【CSS】
:root {
--shift: 0px; /* JSが渡すずれ量(px) */
--skew: 0deg; /* 速度に応じた歪み */
--radius: 12px;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background:
radial-gradient(120% 120% at 50% 10%, #15131f 0%, #07060b 70%);
font-family: "Courier New", ui-monospace, monospace;
}
.stage { padding: 24px; }
/* SVGフィルタ定義はレイアウトに影響させない */
.rgb__defs { position: absolute; width: 0; height: 0; pointer-events: none; }
.rgb {
position: relative;
width: min(68vw, 400px);
aspect-ratio: 4 / 3;
margin: 0;
border-radius: var(--radius);
overflow: hidden;
cursor: crosshair;
outline: none;
background: #000;
box-shadow: 0 20px 50px -18px rgba(0, 0, 0, .9);
}
.rgb:focus-visible { box-shadow: 0 0 0 3px #00e5ff, 0 20px 50px -18px rgba(0, 0, 0, .9); }
/* 3レイヤーを重ね、screen 合成で加法混色(重なれば元の色に戻る) */
.rgb__layer {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
mix-blend-mode: screen;
will-change: transform;
}
/* 各チャンネルだけ残す(他2色を落とす)色行列フィルタ */
.rgb__layer--r { filter: url(#only-r); }
.rgb__layer--g { filter: url(#only-g); }
.rgb__layer--b { filter: url(#only-b); }
/* R は左、B は右へ。G は基準。--shift をJSで増減 */
.rgb__layer--r { transform: translateX(calc(var(--shift) * -1)) skewX(var(--skew)); }
.rgb__layer--g { transform: translateX(0); }
.rgb__layer--b { transform: translateX(var(--shift)) skewX(calc(var(--skew) * -1)); }
/* 走査線。ずれが大きいほど濃く(--scan をJSで) */
.rgb::after {
content: "";
position: absolute;
inset: 0;
background: repeating-linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, .14) 2px,
rgba(0, 0, 0, 0) 3px
);
pointer-events: none;
opacity: var(--scan, 0);
transition: opacity .25s ease;
}
.rgb__cap {
position: absolute;
left: 14px;
bottom: 12px;
z-index: 3;
font-size: 16px;
font-weight: 700;
letter-spacing: .5em;
color: #fff;
text-shadow: 2px 0 #ff003c, -2px 0 #00e5ff;
pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
.rgb__layer { transition: none; }
}
【JavaScript】
// マウスの移動量(速度)に応じて R/G/B のずれ量を CSS 変数で増減する
(() => {
const rgb = document.querySelector(".rgb");
if (!rgb) return;
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
const MAX = 14; // 最大ずれ量(px)
const HOVER_BASE = 4; // ホバー中の最低ずれ量(px)
let shift = 0; // 現在のずれ量
let target = 0; // 目標ずれ量
let lastX = 0, lastY = 0, hasLast = false;
let active = false;
let raf = 0;
// 速度→ずれ量。動かすほど大きく、止めれば HOVER_BASE へ戻る
const onMove = (e) => {
active = true;
if (hasLast) {
const dx = e.clientX - lastX;
const dy = e.clientY - lastY;
const speed = Math.min(Math.hypot(dx, dy), 60);
target = Math.min(HOVER_BASE + speed * 0.45, MAX);
} else {
target = HOVER_BASE;
}
lastX = e.clientX;
lastY = e.clientY;
hasLast = true;
start();
};
const onEnter = () => { active = true; target = HOVER_BASE; start(); };
const onLeave = () => { active = false; target = 0; hasLast = false; };
const onFocus = () => { active = true; target = HOVER_BASE + 4; start(); };
const onBlur = () => { active = false; target = 0; };
rgb.addEventListener("pointermove", onMove);
rgb.addEventListener("pointerenter", onEnter);
rgb.addEventListener("pointerleave", onLeave);
rgb.addEventListener("focus", onFocus);
rgb.addEventListener("blur", onBlur);
// ホバー中はじわじわ減衰(止めても完全には消えず微振動)
const tick = () => {
// ホバー継続中は target を少しずつ HOVER_BASE に戻す
if (active && target > HOVER_BASE) target += (HOVER_BASE - target) * 0.06;
shift += (target - shift) * 0.2;
const skew = (shift / MAX) * 2.5; // ずれに比例した skew(deg)
rgb.style.setProperty("--shift", `${shift.toFixed(2)}px`);
rgb.style.setProperty("--skew", `${skew.toFixed(2)}deg`);
rgb.style.setProperty("--scan", `${Math.min(shift / 6, 1).toFixed(2)}`);
// ほぼ静止したらループ停止(非アクティブ時のみ)
if (!active && shift < 0.05) {
shift = 0;
rgb.style.setProperty("--shift", "0px");
rgb.style.setProperty("--skew", "0deg");
rgb.style.setProperty("--scan", "0");
raf = 0;
return;
}
raf = requestAnimationFrame(tick);
};
const start = () => { if (!raf && !reduce) raf = requestAnimationFrame(tick); };
// reduce 指定時はホバーで固定ずれのみ(アニメ無し)
if (reduce) {
rgb.addEventListener("pointerenter", () => {
rgb.style.setProperty("--shift", `${HOVER_BASE}px`);
rgb.style.setProperty("--scan", "0.5");
});
rgb.addEventListener("pointerleave", () => {
rgb.style.setProperty("--shift", "0px");
rgb.style.setProperty("--scan", "0");
});
}
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。