ギャラリー · ギャラリー

ネオン看板の夜景写真ギャラリー

保存した一覧を見る 0 →

このブラウザー内に保存します。別の端末との同期はなく、サイトデータを消すと保存も消えます。

印刷時は見本を静止画にし、プロンプトと解説を出力します。PDF保存はブラウザーの印刷画面から選べます。

Vibehaus · デザイン資料
https://vibe.itlibra.com/design/neon-night-gallery/
完全無料・著作権フリー(CC0)· 料金¥0 · 商用利用・改変自由

プレビュー

画面に合わせる

指定幅で実際に組み替えて表示します。表示領域より広い場合は、この枠の中で横へスクロールできます。

vibe.itlibra.com/preview/neon-night-gallery/

AIへの指示文

ネオン看板を撮った夜景写真の、ギャラリーページを作ってください。

要件:
- 地は #0a0a12、写真を載せる面は #12121d。文字は #eceaf6、補助の文字は #8e8aa6。
- 光の色はピンク #ff3d9a・水色 #22e4ff・橙 #ffb547・紫 #b36bff の4色。
- 見出し「NIGHT SIGNS」はネオン管の文字にする。text-shadow を4段重ね(白の芯と、ピンクの光を広さ違いで3つ)。
- 点滅はさせない。4秒かけて明るさがわずかに揺れるだけの動きに留め、prefers-reduced-motion では止める。
- 見出しの下に、撮影方針を1文。右側に等幅の文字で写真の枚数(絞り込みに合わせて変わる。aria-live="polite")。
- 「すべて・新宿・大阪・香港」の絞り込みボタン。aria-pressed で選択中を示し、選択中だけ水色の光る枠にする。
- 写真は columns で3列に並べ、縦横比の違う写真を列の中で縦に詰める。break-inside: avoid。
- 写真の縁は、ホバーしたときだけその写真の看板の色で光る。普段は暗い枠線。
- 写真の下に、撮影場所と時刻(等幅)を1行で。
- 写真がまだ無い段階では、暗い路地に色の塊と縦長の看板の光を CSS で描いて仮置きする。
- 本文の字間は 0、行間は 1.8。

バリエーション

大きく1枚を見せる版

写真をクリックすると画面いっぱいに拡大表示する。背景は #0a0a12 の90%不透明、
Esc と閉じるボタンで戻れるようにし、開いている間は背後のスクロールを止める。

昼の街版

地を #f4f2ee の明るい灰色に、見出しの光を消して太い黒の文字にする。
写真の縁の光もやめ、ホバーで写真を少し拡大するだけにする。

このプレビューのコード

HTML・CSS・JavaScriptの1ファイルです。外部依存・広告・解析コードはありません。CSSで描く6作品を3地域で絞り込む見本です。実件数と解除を確認できます。実際の写真・撮影記録・外部取得・送信・保存は含みません。演出は停止できます。

CC0 · 改変・商用利用自由 · revision 40d73678929f

コードを表示
<!doctype html>
<!-- Vibehaus · CC0 · 架空の表示・操作見本。外部素材・送信・永続保存なし。 -->
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="robots" content="noindex" />
    <title>ネオン看板の夜景写真ギャラリー — Vibehaus プレビュー</title>
    <style>
      :root {
        --bg: #0a0a12;
        --panel: #12121d;
        --text: #eceaf6;
        --muted: #8e8aa6;
        --pink: #ff3d9a;
        --cyan: #22e4ff;
        --amber: #ffb547;
        --gothic: "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic", "Meiryo", sans-serif;
        --mono: "JetBrains Mono", Menlo, Consolas, "Hiragino Sans", "Yu Gothic", monospace;
      }
      * { box-sizing: border-box; }
      body {
        margin: 0;
        font-family: var(--gothic);
        line-height: 1.8;
        letter-spacing: 0;
        color: var(--text);
        background: var(--bg);
      }
      .wrap {
        max-width: 78rem;
        margin: 0 auto;
        padding: clamp(2rem, 5vw, 4rem) clamp(1rem, 4vw, 2.5rem) 4rem;
      }

      /* ---- 見出し: ネオン管の文字。光は text-shadow を重ねるだけ ---- */
      header {
        display: flex;
        flex-wrap: wrap;
        align-items: end;
        justify-content: space-between;
        gap: 1.5rem;
        margin-bottom: 2rem;
      }
      .title {
        margin: 0;
        font-size: clamp(2.2rem, 6vw, 4rem);
        font-weight: 800;
        line-height: 1.3;
        color: #ffe6f3;
        text-shadow:
          0 0 4px #fff,
          0 0 12px var(--pink),
          0 0 28px var(--pink),
          0 0 56px rgb(255 61 154 / 0.6);
        /* 点滅はさせない。4秒かけてわずかに明るさが揺れるだけ */
        animation: hum 4s ease-in-out infinite alternate;
      }
      @keyframes hum {
        from { opacity: 1; }
        to { opacity: 0.88; }
      }
      .sub {
        margin: 0.4rem 0 0;
        color: var(--muted);
        font-size: 0.92rem;
      }
      .count {
        font-family: var(--mono);
        font-size: 0.8rem;
        letter-spacing: 0.12em;
        color: var(--cyan);
      }

      /* ---- 絞り込み ---- */
      .filters {
        display: flex;
        flex-wrap: wrap;
        gap: 0.5rem;
        margin: 0 0 1.75rem;
        padding: 0;
        list-style: none;
      }
      .filters button {
        padding: 0.45rem 1rem;
        border: 1px solid #2c2a40;
        border-radius: 999px;
        background: transparent;
        color: var(--muted);
        font: inherit;
        font-size: 0.85rem;
        cursor: pointer;
        transition: color 0.2s, border-color 0.2s, box-shadow 0.2s;
      }
      .filters button[aria-pressed="true"] {
        color: var(--text);
        border-color: var(--cyan);
        box-shadow: 0 0 0 1px var(--cyan), 0 0 16px -2px var(--cyan);
      }
      .filters button:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }

      /* ---- 写真の並び: 列の中で縦に詰める ---- */
      .grid {
        columns: 3 18rem;
        column-gap: 1rem;
      }
      .shot {
        break-inside: avoid;
        margin: 0 0 1rem;
        border-radius: 10px;
        overflow: hidden;
        background: var(--panel);
        border: 1px solid #1f1d30;
        transition: border-color 0.25s, box-shadow 0.25s, transform 0.25s;
      }
      .shot:hover {
        border-color: var(--glow);
        box-shadow: 0 0 0 1px var(--glow), 0 12px 40px -12px var(--glow);
        transform: translateY(-2px);
      }
      .shot[hidden] { display: none; }

      /* 写真の代わりに、夜の路地と看板を色の塊で描く。実案件ではここを <img> に差し替える */
      .photo {
        position: relative;
        aspect-ratio: var(--ratio, 4 / 5);
        background:
          radial-gradient(18% 10% at var(--x1, 30%) var(--y1, 30%), var(--c1), transparent 70%),
          radial-gradient(14% 22% at var(--x2, 70%) var(--y2, 45%), var(--c2), transparent 70%),
          radial-gradient(40% 20% at 50% 100%, rgb(255 255 255 / 0.08), transparent 70%),
          linear-gradient(180deg, #05050a 0%, #141427 60%, #0b0b14 100%);
      }
      /* 看板の縦長の光。濡れた路面への映り込みも添える */
      .photo::before,
      .photo::after {
        content: "";
        position: absolute;
        left: var(--sx, 22%);
        width: 9%;
        border-radius: 3px;
        background: var(--c1);
        box-shadow: 0 0 18px 2px var(--c1);
      }
      .photo::before { top: 18%; height: 34%; }
      .photo::after { bottom: 4%; height: 14%; opacity: 0.25; filter: blur(3px); }

      figcaption {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        padding: 0.8rem 1rem;
        font-size: 0.85rem;
      }
      figcaption span {
        font-family: var(--mono);
        font-size: 0.72rem;
        letter-spacing: 0.08em;
        color: var(--muted);
        white-space: nowrap;
      }

      .p1 { --c1: #ff3d9a; --c2: #22e4ff; --glow: #ff3d9a; --ratio: 4 / 5; }
      .p2 { --c1: #22e4ff; --c2: #ffb547; --glow: #22e4ff; --ratio: 1 / 1; --sx: 64%; }
      .p3 { --c1: #ffb547; --c2: #ff3d9a; --glow: #ffb547; --ratio: 3 / 4; --x1: 70%; }
      .p4 { --c1: #b36bff; --c2: #22e4ff; --glow: #b36bff; --ratio: 4 / 3; --sx: 40%; }
      .p5 { --c1: #ff3d9a; --c2: #ffb547; --glow: #ff3d9a; --ratio: 2 / 3; --sx: 70%; }
      .p6 { --c1: #22e4ff; --c2: #b36bff; --glow: #22e4ff; --ratio: 4 / 5; --x2: 25%; }

      @media (prefers-reduced-motion: reduce) {
        .title { animation: none; }
        .shot, .filters button { transition: none; }
        .shot:hover { transform: none; }
      }
    [hidden]{display:none!important}
a,button,input,summary{min-width:44px;min-height:44px}
a{touch-action:manipulation}
button{cursor:pointer}
button:disabled{cursor:default}
a:focus-visible,button:focus-visible,input:focus-visible{outline:3px solid currentColor;outline-offset:4px}
nav a{display:inline-flex;align-items:center;justify-content:center}
nav{flex-wrap:wrap}
header.bar{flex-wrap:wrap;gap:1rem}
input{max-width:100%}
.demo-notes{position:relative;z-index:10;box-sizing:border-box;max-width:1080px;width:calc(100% - 2rem);margin:2rem auto;padding:clamp(1rem,3vw,2rem);background:#0a0a12;color:#eceaf6;border:1px solid currentColor;text-align:left;font-family:"Hiragino Sans","Yu Gothic",Meiryo,sans-serif;font-size:13px;line-height:1.8;letter-spacing:0;overflow-wrap:anywhere}
.demo-notes h2{font-size:18px;line-height:1.5;margin:0 0 .75rem}
.demo-notes h3{font-size:15px;line-height:1.5;margin:0 0 .4rem}
.demo-notes p{font:inherit;color:inherit;opacity:1;margin:0 0 1rem;max-width:none;letter-spacing:0}
.demo-notes section{margin-top:1.25rem;scroll-margin-top:1rem}
.demo-notes button{font:inherit;color:inherit;background:transparent;border:1px solid currentColor;padding:.4rem .75rem}
.demo-notes a{color:inherit}
.demo-notes .demo-motion{margin-bottom:1rem}
.demo-notes section:focus{outline:3px solid currentColor;outline-offset:4px}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}
}
.shot[hidden]{display:none!important}
.filters{flex-wrap:wrap}
html[data-demo-paused] *,html[data-demo-paused] *::before,html[data-demo-paused] *::after{animation-play-state:paused!important}
html[data-demo-paused] .ticker p{animation:none!important;padding-left:8px;white-space:normal}
</style>
  <noscript><style>*,*::before,*::after{animation:none!important}.ticker p{padding-left:8px;white-space:normal}</style></noscript>
</head>
  <body>
    <div class="wrap">
      <header>
        <div>
          <h1 class="title">NIGHT SIGNS</h1>
          <p class="sub">閉店後も灯り続ける看板を、雨の夜にだけ撮っています。</p>
        </div>
        <p class="count" aria-live="polite"><span id="n">6</span> PHOTOS</p>
      </header>

      <ul class="filters" aria-label="撮影地で絞り込む">
        <li><button type="button" data-js-control disabled data-f="all" aria-pressed="true">すべて</button></li>
        <li><button type="button" data-js-control disabled data-f="shinjuku" aria-pressed="false">新宿</button></li>
        <li><button type="button" data-js-control disabled data-f="osaka" aria-pressed="false">大阪</button></li>
        <li><button type="button" data-js-control disabled data-f="hongkong" aria-pressed="false">香港</button></li>
      </ul>

      <div class="grid">
        <figure class="shot p1" data-area="shinjuku">
          <div class="photo" role="img" aria-label="ピンクの縦看板が濡れた路面に映る路地"></div>
          <figcaption>思い出横丁の奥 <span>23:48</span></figcaption>
        </figure>
        <figure class="shot p2" data-area="osaka">
          <div class="photo" role="img" aria-label="水色の看板と橙色の提灯が並ぶ商店街"></div>
          <figcaption>裏なんばの角 <span>01:12</span></figcaption>
        </figure>
        <figure class="shot p3" data-area="hongkong">
          <div class="photo" role="img" aria-label="橙色の看板が張り出す雨の大通り"></div>
          <figcaption>油麻地の交差点 <span>22:05</span></figcaption>
        </figure>
        <figure class="shot p4" data-area="shinjuku">
          <div class="photo" role="img" aria-label="紫の光が差し込む高架下"></div>
          <figcaption>大ガード下 <span>00:31</span></figcaption>
        </figure>
        <figure class="shot p5" data-area="hongkong">
          <div class="photo" role="img" aria-label="ピンクと橙の看板が重なる細い坂道"></div>
          <figcaption>廟街の坂 <span>02:40</span></figcaption>
        </figure>
        <figure class="shot p6" data-area="osaka">
          <div class="photo" role="img" aria-label="水色の看板が川面に映る夜の橋"></div>
          <figcaption>道頓堀の橋 <span>23:59</span></figcaption>
        </figure>
      </div>
    </div>


  <aside class="demo-notes" aria-label="この見本について">
<h2>この見本について</h2>
<p>CSSで描く架空の作品集です。写真の撮影実績や画像ファイルは含みません。作品リンクは構成の説明へ移動し、投稿・アップロード・保存は行いません。配布HTMLはCC0・利用料¥0。外部素材やライブラリの取得は不要です。</p>
<div class="demo-motion">
<button type="button" data-demo-motion data-js-control disabled aria-pressed="false">演出を止める</button>
</div>
<noscript>
<p>JavaScriptなしでも初期表示と説明リンクを利用できます。絞り込み・入力確認・演出の切替は無効です。</p>
</noscript>
</aside><script>
const filters=[...document.querySelectorAll('[data-f]')],tiles=[...document.querySelectorAll('.shot')];filters.forEach(button=>button.addEventListener('click',()=>{filters.forEach(b=>b.setAttribute('aria-pressed',String(b===button)));let count=0;tiles.forEach(tile=>{tile.hidden=button.dataset.f!=='all'&&tile.dataset.area!==button.dataset.f;if(!tile.hidden)count++});document.querySelector('#n').textContent=count}));const motion=matchMedia('(prefers-reduced-motion: reduce)'),toggle=document.querySelector('[data-demo-motion]');let paused=false;function syncMotion(){const stop=paused||motion.matches;document.documentElement.toggleAttribute('data-demo-paused',stop);toggle.setAttribute('aria-pressed',String(stop));toggle.disabled=motion.matches;toggle.textContent=motion.matches?'動きを減らす設定で停止中':stop?'演出を再開':'演出を止める'}toggle.addEventListener('click',()=>{paused=!paused;syncMotion()});motion.addEventListener('change',syncMotion);
document.querySelectorAll('[data-js-control]').forEach(el=>el.disabled=false);syncMotion();
</script>
</body>
</html>

2026年10月14日、Chromiumで320・390・768・1280pxの描画とキーボードのフォーカス表示を確認しました。他のブラウザは未確認です。利用先のブラウザと実データでも確認してください。

概要

ネオン看板は、夜の街でいちばん強い光です。そのネオンを撮った写真を並べるギャラリーで、画面まで光らせると写真と画面が同じ強さで光り合い、どちらも見えなくなります。このデザインは、光る場所を見出しと、選んでいるものだけに絞っています。

光らせる場所を3つに絞る

ネオン風のデザインは、光の効果を足すほどそれらしく見えます。ただしギャラリーでは、主役は写真の中の光です。

崩れる形すべての枠と文字を光らせる

画面全体が明るくなり、写真の中の看板が目立たなくなります。どこを押せるのかも光では分からなくなります。

効く形見出し・選択中・ホバー中だけ光らせる

光がいま注目している場所の合図になります。暗い地に写真だけが浮かび、看板の色が引き立ちます。

写真の縁が光る色は、その写真に写っている看板の色に合わせています。ピンクの看板の写真はピンクに、水色の看板は水色に。ホバーした写真の色がそのまま画面に広がるので、1枚ずつ見比べる楽しさが生まれます。

動きは、ゆっくり揺れるだけ

本物のネオン管は、古くなるとちらつきます。その再現は魅力的ですが、速い点滅は光に敏感な人に発作を引き起こすおそれがあります。

見出しは4秒かけて明るさが1割揺れるだけ。これでも、通電しているネオンの気配は十分に出ます。

仕上げの判断軸

4色ピンク・水色・橙・紫。写真ごとに1色を割り当てる
4段見出しの text-shadow。白の芯と光3つ
3列columns で縦に詰める。狭い画面では自動で減る

写真が増えてきたら、絞り込みの項目を増やす前に並び順を見直してください。columns は上から下へ順に詰めるので、新しい写真を先頭に置くと左の列にばかり集まります。

見本で試せる範囲

CSSで描く6作品を3地域で絞り込む見本です。実件数と解除を確認できます。実際の写真・撮影記録・外部取得・送信・保存は含みません。演出は停止できます。 作品・記事・サービス名や数値は表示のための設定です。リンク先には、このHTMLに含まれる範囲と利用先で接続する処理を説明しています。配布HTMLはCC0・利用料¥0で、保存した1ファイルだけで表示できます。

ギャラリーの構成要素

ギャラリーの構成要素 絞り込み・件数 width と height を書いて、読み込み前に場所を確保する 書かないと、画像が届くたびに下の内容が突き落とされる
ギャラリーは画像の枚数だけレイアウトのずれが起きます。寸法の申告と遅延読み込みは、見た目より先に手当てしてください。

使いどころ

  • 写真家や映像作家のポートフォリオの作品一覧
  • 夜の街・飲食店街・イベントの写真をまとめた特設ページ
  • 音楽やゲームのビジュアル素材を並べるページ
  • 撮影地や種類で絞り込みたい、枚数の多い写真集

このデザインの分類

カテゴリ
ギャラリー
スタイル
ネオン / ダーク / サイバー

背景と狙いどころ

よくある質問

ネオンの光を写真の上にも重ねたほうが雰囲気が出ませんか?
写真そのものが光っているので、上から重ねると色が濁ります。光らせるのは見出しと、選んでいる絞り込みボタン、ホバーした写真の縁だけにしています。
見出しをちらつかせるとネオンらしくなりませんか?
速い点滅は、光に敏感な人に発作を引き起こすおそれがあります。ここでは4秒かけて明るさが1割ほど揺れるだけにしていて、動きを減らす設定の人には止まります。
写真の並べ方に columns を使うのはなぜですか?
縦長と横長の写真が混ざっても、列の中で上から順に詰まっていくからです。grid で同じ見た目を作るには行の高さの計算が要ります。並び順が縦方向になる点だけは気をつけてください。

この画面を実装する

動くサンプルとコピペできるコードを、技術別にまとめています。