L-010 Layout medium

Editorial Expanding Panels エディトリアル・エクスパンディングパネル

High-contrast editorial typography panels that expand upon interaction with fluid CSS sizing, full keyboard navigation, and zero image dependencies. 写真を使わずに大胆なタイポグラフィとキーボード対応アコーディオンで魅せるエディトリアル展開パネル。

ライブデモ Live Demo

STRUCTURE & SILENCE

Negative space serves as structural concrete in modern layouts. Removing the decorative leaves only rhythm and intentional hierarchy.

EDITORIALSWISS MODERN

MONOCHROME DEPTH

Color applied sparingly exerts tenfold psychological leverage. A solitary scarlet accent directs eye velocity across dense editorial columns.

COLOR STUDYTHEORY

VARIABLE CADENCE

Text that breathes with the viewport redefines reading stamina. Proportions shift fluidly between editorial gravitas and instant scanning.

DYNAMICSINTERFACE

概要・用途・特徴Overview, Usage & Features

何ができるかWhat it does

High-contrast editorial typography panels that expand upon interaction with fluid CSS sizing, full keyboard navigation, and zero image dependencies.

写真を使わずに大胆なタイポグラフィとキーボード対応アコーディオンで魅せるエディトリアル展開パネル。

どこで使うかWhere to use

Portfolio, product showcase, landing page.

ポートフォリオ、製品紹介、ランディングページ。

特徴Key features

High-contrast editorial typography panels that expand upon interaction with fluid CSS sizing, full keyboard navigation, and zero image dependencies.

写真を使わずに大胆なタイポグラフィとキーボード対応アコーディオンで魅せるエディトリアル展開パネル。

調整可能パラメータ Adjustable Parameters

Parameter Default Description
--ep-accent#ff3b30Change this CSS custom property to match your brand color.

実装コード Implementation Code

<div class="demo-stage ep-root"><div class="ep-wrapper" role="region" aria-label="Editorial Articles"><article class="ep-panel ep-active"><button type="button" class="ep-trigger" aria-expanded="true" aria-label="Expand article 01: Typographic Architecture"><span class="ep-num">01</span><span class="ep-label">ARCHITECTURE</span></button><div class="ep-content"><h3 class="ep-heading">STRUCTURE &amp; SILENCE</h3><p class="ep-text">Negative space serves as structural concrete in modern layouts. Removing the decorative leaves only rhythm and intentional hierarchy.</p><div class="ep-tags"><span class="ep-tag">EDITORIAL</span><span class="ep-tag">SWISS MODERN</span></div></div></article><article class="ep-panel"><button type="button" class="ep-trigger" aria-expanded="false" aria-label="Expand article 02: Chromatic Resonance"><span class="ep-num">02</span><span class="ep-label">RESONANCE</span></button><div class="ep-content"><h3 class="ep-heading">MONOCHROME DEPTH</h3><p class="ep-text">Color applied sparingly exerts tenfold psychological leverage. A solitary scarlet accent directs eye velocity across dense editorial columns.</p><div class="ep-tags"><span class="ep-tag">COLOR STUDY</span><span class="ep-tag">THEORY</span></div></div></article><article class="ep-panel"><button type="button" class="ep-trigger" aria-expanded="false" aria-label="Expand article 03: Kinetic Legibility"><span class="ep-num">03</span><span class="ep-label">LEGIBILITY</span></button><div class="ep-content"><h3 class="ep-heading">VARIABLE CADENCE</h3><p class="ep-text">Text that breathes with the viewport redefines reading stamina. Proportions shift fluidly between editorial gravitas and instant scanning.</p><div class="ep-tags"><span class="ep-tag">DYNAMICS</span><span class="ep-tag">INTERFACE</span></div></div></article></div></div>
<script>
(() => { document.querySelectorAll('.ep-root').forEach((root) => { const panels = root.querySelectorAll('.ep-panel'); const triggers = root.querySelectorAll('.ep-trigger'); if (!triggers.length) return; const select = (idx) => { panels.forEach((p, i) => { const active = i === idx; p.classList.toggle('ep-active', active); p.querySelector('.ep-content')?.setAttribute('aria-hidden', String(!active)); const btn = p.querySelector('.ep-trigger'); if (btn) btn.setAttribute('aria-expanded', String(active)); }); }; triggers.forEach((btn, idx) => { btn.addEventListener('click', () => select(idx)); btn.addEventListener('keydown', (e) => { let next = -1; if (e.key === 'ArrowRight' || e.key === 'ArrowDown') next = (idx + 1) % triggers.length; if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') next = (idx - 1 + triggers.length) % triggers.length; if (next >= 0) { e.preventDefault(); triggers[next].focus(); select(next); } }); }); }); })();
document.querySelectorAll(".ep-root").forEach(root=>root.querySelectorAll(".ep-panel").forEach(p=>p.querySelector(".ep-content").setAttribute("aria-hidden",String(!p.classList.contains("ep-active")))));
</script>
.ep-root{--ep-bg:#0a0a0a;--ep-card-bg:#161616;--ep-accent:#ff3b30;--ep-text:#f5f5f7;--ep-sub:#8e8e93;display:flex;align-items:center;justify-content:center;min-height:380px;padding:24px 16px;background:var(--ep-bg);color:var(--ep-text);font-family:"Helvetica Neue",Arial,sans-serif;box-sizing:border-box;width:100%}.ep-root *,.ep-root *::before,.ep-root *::after{box-sizing:border-box}.ep-root .ep-wrapper{display:flex;flex-direction:row;gap:12px;width:100%;max-width:760px;height:320px}.ep-root .ep-panel{flex:1;background:var(--ep-card-bg);border:1px solid #2c2c2e;border-radius:14px;overflow:hidden;position:relative;display:flex;flex-direction:row;transition:flex 0.4s cubic-bezier(0.16,1,0.3,1),background 0.3s;box-shadow:0 10px 30px rgba(0,0,0,0.5)}.ep-root .ep-panel:hover{background:#1c1c1e}.ep-root .ep-panel.ep-active{flex:3.2;background:#18181b;border-color:var(--ep-accent)}.ep-root .ep-trigger{background:transparent;border:none;color:inherit;cursor:pointer;padding:24px 14px;display:flex;flex-direction:column;align-items:center;justify-content:space-between;width:68px;min-width:68px;border-right:1px solid rgba(255,255,255,0.06);outline-offset:-2px}.ep-root .ep-trigger:focus-visible{outline:2px solid var(--ep-accent)}.ep-root .ep-num{font-family:monospace;font-size:18px;font-weight:700;color:var(--ep-accent);letter-spacing:1px}.ep-root .ep-label{writing-mode:vertical-rl;transform:rotate(180deg);font-size:12px;font-weight:800;letter-spacing:3px;color:var(--ep-sub);margin:auto 0}.ep-root .ep-panel.ep-active .ep-label{color:var(--ep-text)}.ep-root .ep-content{flex:1;padding:32px 28px;display:flex;flex-direction:column;justify-content:center;opacity:0;transform:translateX(16px);transition:opacity 0.25s ease,transform 0.35s ease;pointer-events:none;overflow:hidden}.ep-root .ep-panel.ep-active .ep-content{opacity:1;transform:translateX(0);pointer-events:auto;transition-delay:0.1s}.ep-root .ep-heading{font-size:24px;font-weight:900;letter-spacing:-0.5px;margin:0 0 12px 0;line-height:1.2;color:#ffffff}.ep-root .ep-text{font-size:13px;line-height:1.6;color:#a1a1aa;margin:0 0 24px 0}.ep-root .ep-tags{display:flex;gap:8px}.ep-root .ep-tag{font-size:10px;font-weight:700;letter-spacing:1px;padding:4px 10px;border-radius:6px;background:rgba(255,255,255,0.08);color:#d4d4d8}@media (max-width:620px){.ep-root .ep-wrapper{flex-direction:column;height:420px}.ep-root .ep-panel{flex-direction:column}.ep-root .ep-trigger{width:100%;min-width:unset;height:48px;flex-direction:row;border-right:none;border-bottom:1px solid rgba(255,255,255,0.06);padding:10px 16px}.ep-root .ep-label{writing-mode:horizontal-tb;transform:none}.ep-root .ep-content{padding:20px 16px}.ep-root .ep-heading{font-size:18px}.ep-root .ep-text{font-size:12px;margin-bottom:14px}}@media (prefers-reduced-motion: no-preference){.ep-root .ep-panel.ep-active{transition:flex 0.45s cubic-bezier(0.16,1,0.3,1)}}
@media not all and (prefers-reduced-motion: no-preference){.ep-root *{transition:none!important;animation:none!important}}
.ep-root .ep-panel{min-width:0}.ep-root .ep-content{min-width:0}.ep-root .ep-tags{flex-wrap:wrap}@media(max-width:620px){.ep-root .ep-wrapper{height:auto;min-height:420px}.ep-root .ep-panel{flex:none}.ep-root .ep-trigger{flex-shrink:0}.ep-root .ep-panel:not(.ep-active) .ep-content{display:none}.ep-root .ep-panel.ep-active{flex:1}.ep-root .ep-content{overflow:visible}}
@media(min-width:621px){.ep-root .ep-panel:not(.ep-active){flex:0 0 68px}.ep-root .ep-panel:not(.ep-active) .ep-content{display:none}.ep-root .ep-panel.ep-active{flex:1}}
import { useState } from "react";
import "./L-010.css";
function EditorialExpandingPanels() {
  const [selected, setSelected] = useState(0);
  return <div className="demo-stage ep-root"><div className="ep-wrapper" role="region" aria-label="Editorial Articles"><article className={"ep-panel" + (selected === 0 ? " ep-active" : "")}><button type="button" className="ep-trigger" aria-label="Expand article 01: Typographic Architecture" aria-expanded={selected === 0} onClick={() => setSelected(0)} onKeyDown={(event) => {
    let next = 0;
    if (["ArrowRight", "ArrowDown"].includes(event.key)) next = (0 + 1) % 3;
    else if (["ArrowLeft", "ArrowUp"].includes(event.key)) next = (0 + 2) % 3;
    else return;
    event.preventDefault();
    setSelected(next);
    event.currentTarget.closest(".ep-root").querySelectorAll(".ep-trigger")[next].focus();
  }}><span className="ep-num">{"01"}</span><span className="ep-label">{"ARCHITECTURE"}</span></button><div className="ep-content" aria-hidden={selected !== 0}><h3 className="ep-heading">{"STRUCTURE & SILENCE"}</h3><p className="ep-text">{"Negative space serves as structural concrete in modern layouts. Removing the decorative leaves only rhythm and intentional hierarchy."}</p><div className="ep-tags"><span className="ep-tag">{"EDITORIAL"}</span><span className="ep-tag">{"SWISS MODERN"}</span></div></div></article><article className={"ep-panel" + (selected === 1 ? " ep-active" : "")}><button type="button" className="ep-trigger" aria-label="Expand article 02: Chromatic Resonance" aria-expanded={selected === 1} onClick={() => setSelected(1)} onKeyDown={(event) => {
    let next = 1;
    if (["ArrowRight", "ArrowDown"].includes(event.key)) next = (1 + 1) % 3;
    else if (["ArrowLeft", "ArrowUp"].includes(event.key)) next = (1 + 2) % 3;
    else return;
    event.preventDefault();
    setSelected(next);
    event.currentTarget.closest(".ep-root").querySelectorAll(".ep-trigger")[next].focus();
  }}><span className="ep-num">{"02"}</span><span className="ep-label">{"RESONANCE"}</span></button><div className="ep-content" aria-hidden={selected !== 1}><h3 className="ep-heading">{"MONOCHROME DEPTH"}</h3><p className="ep-text">{"Color applied sparingly exerts tenfold psychological leverage. A solitary scarlet accent directs eye velocity across dense editorial columns."}</p><div className="ep-tags"><span className="ep-tag">{"COLOR STUDY"}</span><span className="ep-tag">{"THEORY"}</span></div></div></article><article className={"ep-panel" + (selected === 2 ? " ep-active" : "")}><button type="button" className="ep-trigger" aria-label="Expand article 03: Kinetic Legibility" aria-expanded={selected === 2} onClick={() => setSelected(2)} onKeyDown={(event) => {
    let next = 2;
    if (["ArrowRight", "ArrowDown"].includes(event.key)) next = (2 + 1) % 3;
    else if (["ArrowLeft", "ArrowUp"].includes(event.key)) next = (2 + 2) % 3;
    else return;
    event.preventDefault();
    setSelected(next);
    event.currentTarget.closest(".ep-root").querySelectorAll(".ep-trigger")[next].focus();
  }}><span className="ep-num">{"03"}</span><span className="ep-label">{"LEGIBILITY"}</span></button><div className="ep-content" aria-hidden={selected !== 2}><h3 className="ep-heading">{"VARIABLE CADENCE"}</h3><p className="ep-text">{"Text that breathes with the viewport redefines reading stamina. Proportions shift fluidly between editorial gravitas and instant scanning."}</p><div className="ep-tags"><span className="ep-tag">{"DYNAMICS"}</span><span className="ep-tag">{"INTERFACE"}</span></div></div></article></div></div>;
}
export {
  EditorialExpandingPanels as default
};
.ep-root{--ep-bg:#0a0a0a;--ep-card-bg:#161616;--ep-accent:#ff3b30;--ep-text:#f5f5f7;--ep-sub:#8e8e93;display:flex;align-items:center;justify-content:center;min-height:380px;padding:24px 16px;background:var(--ep-bg);color:var(--ep-text);font-family:"Helvetica Neue",Arial,sans-serif;box-sizing:border-box;width:100%}.ep-root *,.ep-root *::before,.ep-root *::after{box-sizing:border-box}.ep-root .ep-wrapper{display:flex;flex-direction:row;gap:12px;width:100%;max-width:760px;height:320px}.ep-root .ep-panel{flex:1;background:var(--ep-card-bg);border:1px solid #2c2c2e;border-radius:14px;overflow:hidden;position:relative;display:flex;flex-direction:row;transition:flex 0.4s cubic-bezier(0.16,1,0.3,1),background 0.3s;box-shadow:0 10px 30px rgba(0,0,0,0.5)}.ep-root .ep-panel:hover{background:#1c1c1e}.ep-root .ep-panel.ep-active{flex:3.2;background:#18181b;border-color:var(--ep-accent)}.ep-root .ep-trigger{background:transparent;border:none;color:inherit;cursor:pointer;padding:24px 14px;display:flex;flex-direction:column;align-items:center;justify-content:space-between;width:68px;min-width:68px;border-right:1px solid rgba(255,255,255,0.06);outline-offset:-2px}.ep-root .ep-trigger:focus-visible{outline:2px solid var(--ep-accent)}.ep-root .ep-num{font-family:monospace;font-size:18px;font-weight:700;color:var(--ep-accent);letter-spacing:1px}.ep-root .ep-label{writing-mode:vertical-rl;transform:rotate(180deg);font-size:12px;font-weight:800;letter-spacing:3px;color:var(--ep-sub);margin:auto 0}.ep-root .ep-panel.ep-active .ep-label{color:var(--ep-text)}.ep-root .ep-content{flex:1;padding:32px 28px;display:flex;flex-direction:column;justify-content:center;opacity:0;transform:translateX(16px);transition:opacity 0.25s ease,transform 0.35s ease;pointer-events:none;overflow:hidden}.ep-root .ep-panel.ep-active .ep-content{opacity:1;transform:translateX(0);pointer-events:auto;transition-delay:0.1s}.ep-root .ep-heading{font-size:24px;font-weight:900;letter-spacing:-0.5px;margin:0 0 12px 0;line-height:1.2;color:#ffffff}.ep-root .ep-text{font-size:13px;line-height:1.6;color:#a1a1aa;margin:0 0 24px 0}.ep-root .ep-tags{display:flex;gap:8px}.ep-root .ep-tag{font-size:10px;font-weight:700;letter-spacing:1px;padding:4px 10px;border-radius:6px;background:rgba(255,255,255,0.08);color:#d4d4d8}@media (max-width:620px){.ep-root .ep-wrapper{flex-direction:column;height:420px}.ep-root .ep-panel{flex-direction:column}.ep-root .ep-trigger{width:100%;min-width:unset;height:48px;flex-direction:row;border-right:none;border-bottom:1px solid rgba(255,255,255,0.06);padding:10px 16px}.ep-root .ep-label{writing-mode:horizontal-tb;transform:none}.ep-root .ep-content{padding:20px 16px}.ep-root .ep-heading{font-size:18px}.ep-root .ep-text{font-size:12px;margin-bottom:14px}}@media (prefers-reduced-motion: no-preference){.ep-root .ep-panel.ep-active{transition:flex 0.45s cubic-bezier(0.16,1,0.3,1)}}
@media not all and (prefers-reduced-motion: no-preference){.ep-root *{transition:none!important;animation:none!important}}
.ep-root .ep-panel{min-width:0}.ep-root .ep-content{min-width:0}.ep-root .ep-tags{flex-wrap:wrap}@media(max-width:620px){.ep-root .ep-wrapper{height:auto;min-height:420px}.ep-root .ep-panel{flex:none}.ep-root .ep-trigger{flex-shrink:0}.ep-root .ep-panel:not(.ep-active) .ep-content{display:none}.ep-root .ep-panel.ep-active{flex:1}.ep-root .ep-content{overflow:visible}}
@media(min-width:621px){.ep-root .ep-panel:not(.ep-active){flex:0 0 68px}.ep-root .ep-panel:not(.ep-active) .ep-content{display:none}.ep-root .ep-panel.ep-active{flex:1}}

仕組みとカスタマイズHow It Works & Customization

仕組みHow it works

Scoped CSS creates the visual design; small vanilla JavaScript handlers update the local demo state.

スコープ化したCSSで描画し、Vanilla JavaScriptでデモ内の状態だけを更新します。

カスタマイズ方法Customization

Change this CSS custom property to match your brand color.

CSSカスタムプロパティでブランドカラーを変更できます。

注意点Caveats

The sample content is fictional. No external assets or libraries are required. Motion follows the operating system preference. Demo labels are English; surrounding documentation supports Japanese and English.

デモ内容は架空です。外部素材・ライブラリは不要。OSの動きを減らす設定に対応します。デモ内のラベルは英語、解説は日英対応です。

よくある質問 Frequently Asked Questions

Does this demo need external libraries? 外部ライブラリは必要ですか?

The sample content is fictional. No external assets or libraries are required. Motion follows the operating system preference. Demo labels are English; surrounding documentation supports Japanese and English.

デモ内容は架空です。外部素材・ライブラリは不要。OSの動きを減らす設定に対応します。デモ内のラベルは英語、解説は日英対応です。

AIへの指示テンプレート AI Prompt Template

以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.