oshiphoto

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 40a48a69c060164e1c0d784f7a9268e2d13e0447
parent fa47da52e3982c32e899af7434cedc7ea71ebc48
Author: Sunny <kajimalu10@gmail.com>
Date:   Sun,  9 Nov 2025 16:03:17 +0900

button style

Diffstat:
Mindex.html | 1+
Msrc/App.vue | 347+++++++++----------------------------------------------------------------------
Dsrc/components/HelloWorld.vue | 41-----------------------------------------
Dsrc/components/ImageGallery.vue | 0
Dsrc/components/ImageUploader.vue | 0
Dsrc/components/SettingMenu.vue | 0
Dsrc/composables/useAppStorage.ts | 0
Dsrc/composables/useIndexedDB.ts | 0
Msrc/main.ts | 3+++
Msrc/style.css | 391+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
10 files changed, 388 insertions(+), 395 deletions(-)

diff --git a/index.html b/index.html @@ -4,6 +4,7 @@ <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,0" /> <title>推しふぉと!</title> </head> <body> diff --git a/src/App.vue b/src/App.vue @@ -11,18 +11,20 @@ ref="canvasWrapperRef" :class="{ 'has-image': canvasHasImage }" > - <div v_if="!canvasHasImage" class="canvas-placeholder"> + <div v-if="!canvasHasImage" class="canvas-placeholder"> 背景画像を読み込んでください </div> <EditorCanvas ref="editor" /> </div> <div class="canvas-controls"> - <button @click="handleSaveImage" class="save-btn"> - 画像を保存 + <button @click="deleteSelectedOshi" class="btn btn-del-selected" title="選択した推しを削除"> + <span class="material-symbols-rounded">delete</span> + <span class="button-text">削除</span> </button> - <button @click="deleteSelectedOshi" class="delete-selected-btn"> - 選択した推しを削除 + <button @click="handleSaveImage" class="btn btn-save" title="画像を保存"> + <span class="material-symbols-rounded">download</span> + <span class="button-text">保存</span> </button> </div> </div> @@ -30,18 +32,7 @@ <div class="gallery oshi-gallery"> <div class="gallery-header"> <div class="gallery-header-info"> - <h3>推し画像ギャラリー</h3> - <p>(クリックでCanvasに追加 / 保存数: {{ oshiGallery.oshiList.value.length }})</p> - </div> - <div class="gallery-header-input"> - <label for="oshi-upload" class="file-label">画像を追加</label> - <input - id="oshi-upload" - class="file-input" - type="file" - accept="image/png, image/gif" - @change="onOshiFileChange" - /> + <h3>推し画像</h3> <p>(保存数: {{ oshiGallery.oshiList.value.length }})</p> </div> </div> <div class="gallery-container"> @@ -52,8 +43,22 @@ class="thumbnail" @click="addOshiToCanvas(image.data)" /> - <button class="delete-btn" @click="oshiGallery.deleteOshiImage(image.id)">×</button> + <button class="delete-btn" @click="oshiGallery.deleteOshiImage(image.id)" title="推し画像を削除"> + <span class="material-symbols-rounded">close</span> + </button> </li> + <div class="gallery-input"> + <label for="oshi-upload" class="btn" title="推し画像を追加"> + <span class="material-symbols-rounded">add_photo_alternate</span>追加 + </label> + <input + id="oshi-upload" + class="file-input" + type="file" + accept="image/png, image/gif" + @change="onOshiFileChange" + /> + </div> </ul> </div> </div> @@ -61,18 +66,7 @@ <div class="gallery"> <div class="gallery-header"> <div class="gallery-header-info"> - <h3>背景画像ギャラリー</h3> - <p>(クリックで背景セット / 保存数: {{ backgroundGallery.galleryList.value.length }})</p> - </div> - <div class="gallery-header-input"> - <label for="bg-upload" class="file-label">画像を追加</label> - <input - id="bg-upload" - class="file-input" - type="file" - accept="image/*" - @change="onBackgroundFileChange" - /> + <h3>背景画像</h3> <p>(保存数: {{ backgroundGallery.galleryList.value.length }})</p> </div> </div> <div class="gallery-container"> @@ -83,8 +77,22 @@ class="thumbnail" @click="setBackground(image.data)" /> - <button class="delete-btn" @click="backgroundGallery.deleteImage(image.id)">×</button> + <button class="delete-btn" @click="backgroundGallery.deleteImage(image.id)" title="背景画像を削除"> + <span class="material-symbols-rounded">close</span> + </button> </li> + <div class="gallery-input"> + <label for="bg-upload" class="btn" title="背景画像を追加"> + <span class="material-symbols-rounded">add_photo_alternate</span>追加 + </label> + <input + id="bg-upload" + class="file-input" + type="file" + accept="image/*" + @change="onBackgroundFileChange" + /> + </div> </ul> </div> </div> @@ -256,279 +264,3 @@ const onOshiFileChange = (event) => { event.target.value = null; }; </script> - -<style> -/* --- グローバル・リセット --- */ -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -html, body, #app { - height: 100%; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - background-color: #f4f4f9; - color: #333; -} - -/* --- PWA用レイアウト --- */ -#app { - display: flex; - flex-direction: column; - /* スマホの画面全体を使う設定。 - アドレスバーが隠れても高さが追従する - */ - height: 100vh; - height: -webkit-fill-available; /* iOS Safari用 */ -} - -header { - flex-shrink: 0; /* 縮まない */ - padding: 12px 16px; - background-color: #fff; - border-bottom: 1px solid #ddd; - text-align: center; - box-shadow: 0 2px 4px rgba(0,0,0,0.05); -} - -header h1 { - font-size: 1.1rem; - font-weight: 600; -} - -/* メインコンテンツエリア */ -.main-content { - flex-grow: 1; - display: flex; - flex-direction: column; - overflow-y: auto; - gap: 0; - padding-bottom: 16px; - padding: 16px; -} - -@media (min-width: 1024px) { - .main-content { - flex-direction: row; /* PCでは横並び */ - gap: 24px; - overflow-y: hidden; /* PCでは親をスクロールさせない */ - } - - .main-column-left { - flex-grow: 1; /* 左カラム(Canvas)が幅を占める */ - min-width: 0; /* 縮小できるように */ - display: flex; - flex-direction: column; - } - - .main-column-right { - flex-shrink: 0; /* 右カラム(操作部)は縮まない */ - width: 380px; /* 右カラムの幅を固定 */ - height: 100%; /* 親の高さに合わせる */ - overflow-y: auto; /* ★ 右カラムだけをスクロールさせる */ - - /* スクロールバーのスタイル(任意) */ - scrollbar-width: thin; - scrollbar-color: #ccc #f4f4f9; - } - - /* PC時はギャラリー間のマージンを調整 */ - .main-column-right .gallery { - margin-bottom: 24px; - } -} - -/* --- Canvasエリア --- */ -/* --- 左カラム (Canvas周り) --- */ -.canvas-wrapper { - flex-grow: 1; /* PC時に高さを広げる */ - flex-shrink: 0; - width: 100%; - - background-color: #eee; - border-radius: 8px; - box-shadow: inset 0 1px 3px rgba(0,0,0,0.1); - min-height: 250px; - - /* プレースホルダーテキスト用 */ - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; -} - -/* 背景画像がセットされたら、プレースホルダーを隠す */ -.canvas-wrapper.has-image { - background-color: transparent; - min-height: 0; - align-items: flex-start; /* 左上に配置 */ - justify-content: flex-start; -} -.canvas-placeholder { - color: #888; - font-size: 0.9rem; -} -.canvas-wrapper.has-image .canvas-placeholder { - display: none; -} - -.canvas-controls { - flex-shrink: 0; - display: flex; - justify-content: flex-end; /* ボタンを右寄せ */ - gap: 12px; /* ★ ボタン間のスペース */ - margin-top: 12px; - margin-bottom: 16px; - flex-wrap: wrap; -} - -.save-btn { - border: none; - background-color: #007aff; /* iOSの青色 */ - color: white; - padding: 8px 12px; - border-radius: 6px; - font-weight: 500; - cursor: pointer; - font-size: 0.9rem; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} -.save-btn:active { - background-color: #0056b3; -} - -.delete-selected-btn { - border: none; - background-color: #e53935; /* 赤系の削除ボタン */ - color: white; - padding: 8px 12px; - border-radius: 6px; - font-weight: 500; - cursor: pointer; - font-size: 0.9rem; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} -.delete-selected-btn:active { - background-color: #c62828; -} - -/* --- ギャラリー --- */ -.gallery-header { - display: flex; - justify-content: space-between; /* 両端揃え */ - align-items: center; /* 中央揃え */ - margin-bottom: 12px; - gap: 16px; -} -.gallery-header-info { - flex-grow: 1; /* タイトル領域が伸びる */ -} -.gallery-header-input { - flex-shrink: 0; /* ボタンは縮まない */ -} - -.gallery h3 { - font-size: 1rem; - font-weight: 600; - margin: 0; /* マージンリセット */ -} - -.gallery p { - font-size: 0.8rem; - color: #666; - margin: 0; /* マージンリセット */ -} - -.gallery { - flex-shrink: 0; - background-color: #fff; - border-radius: 8px; - padding: 16px; - box-shadow: 0 2px 4px rgba(0,0,0,0.05); - display: flex; - flex-direction: column; - margin-bottom: 16px; -} - -.gallery-container { - /* ギャラリーがはみ出たら横スクロール */ - overflow-x: auto; - -webkit-overflow-scrolling: touch; -} - -.gallery ul { - list-style: none; - display: flex; /* 横並び */ - gap: 12px; - padding-bottom: 8px; /* スクロールバーのための余白 */ -} - -.gallery li { - flex-shrink: 0; /* 縮まない */ - position: relative; - border-radius: 6px; - overflow: hidden; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -.thumbnail { - width: 100px; - height: 100px; - object-fit: cover; - cursor: pointer; - display: block; - background-color: #eee; -} - -.thumbnail:active { - filter: brightness(0.8); -} - -.delete-btn { - position: absolute; - top: 4px; - right: 4px; - width: 24px; - height: 24px; - border-radius: 50%; - background-color: rgba(0,0,0,0.6); - color: white; - border: none; - font-size: 14px; - font-weight: bold; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - line-height: 1; -} - -/* --- ▼▼▼ 修正 (ファイル入力) ▼▼▼ --- */ -/* デフォルトのinputを隠す */ -.file-input { - width: 0.1px; - height: 0.1px; - opacity: 0; - overflow: hidden; - position: absolute; - z-index: -1; -} - -/* ラベルをボタン風にスタイリング */ -.file-label { - border: none; - background-color: #007aff; - color: white; - padding: 8px 12px; - border-radius: 6px; - font-weight: 500; - cursor: pointer; - transition: background-color 0.2s; - font-size: 0.9rem; - display: inline-block; -} -.file-label:active { - background-color: #0056b3; -} -</style>- \ No newline at end of file diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue @@ -1,41 +0,0 @@ -<script setup lang="ts"> -import { ref } from 'vue' - -defineProps<{ msg: string }>() - -const count = ref(0) -</script> - -<template> - <h1>{{ msg }}</h1> - - <div class="card"> - <button type="button" @click="count++">count is {{ count ** 10 }}</button> - <p> - Edit - <code>components/HelloWorld.vue</code> to test HMR - </p> - </div> - - <p> - Check out - <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank" - >create-vue</a - >, the official Vue + Vite starter - </p> - <p> - Learn more about IDE Support for Vue in the - <a - href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support" - target="_blank" - >Vue Docs Scaling up Guide</a - >. - </p> - <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p> -</template> - -<style scoped> -.read-the-docs { - color: #888; -} -</style> diff --git a/src/components/ImageGallery.vue b/src/components/ImageGallery.vue diff --git a/src/components/ImageUploader.vue b/src/components/ImageUploader.vue diff --git a/src/components/SettingMenu.vue b/src/components/SettingMenu.vue diff --git a/src/composables/useAppStorage.ts b/src/composables/useAppStorage.ts diff --git a/src/composables/useIndexedDB.ts b/src/composables/useIndexedDB.ts diff --git a/src/main.ts b/src/main.ts @@ -2,4 +2,7 @@ import { createApp } from 'vue' import App from './App.vue' +// グローバルスタイルを読み込む +import './style.css' + createApp(App).mount('#app') diff --git a/src/style.css b/src/style.css @@ -1,7 +1,13 @@ +/* ===== Fonts & Root ===== */ +@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&display=swap"); + :root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; + /* Root font and rendering settings */ + font-family: "Noto Sans JP", sans-serif; + font-optical-sizing: auto; font-weight: 400; + font-style: normal; + line-height: 1.5; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); @@ -13,67 +19,360 @@ -moz-osx-font-smoothing: grayscale; } -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; +/* Light theme overrides */ +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } } -body { +/* ===== Reset ===== */ +/* Basic reset to normalize box sizing and remove default spacing */ +* { + box-sizing: border-box; margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + padding: 0; } -h1 { - font-size: 3.2em; - line-height: 1.1; +button { + font-family: inherit; } -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; +/* ===== Base / Layout ===== */ +/* Base page container and defaults */ +html, +body, +#app { + height: 100%; font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + background-color: #f4f4f9; + color: #333; } -button:hover { - border-color: #646cff; + +/* App shell for PWA-style layout */ +#app { + display: flex; + flex-direction: column; + height: 100vh; + height: -webkit-fill-available; /* iOS Safari */ } -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + +/* Header */ +header { + flex-shrink: 0; + padding: 6px; + background-color: #fff; + border-bottom: 1px solid #ddd; + text-align: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } -.card { - padding: 2em; +header h1 { + font-size: 1.2rem; + font-weight: 600; } -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; +/* ===== Main content & columns ===== */ +.main-content { + flex-grow: 1; + display: flex; + flex-direction: column; + overflow-y: auto; + gap: 0; + padding-bottom: 16px; + padding: 16px; } -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; +/* Desktop columns */ +@media (min-width: 1024px) { + .main-content { + flex-direction: row; /* side-by-side layout */ + gap: 24px; + overflow-y: hidden; } - a:hover { - color: #747bff; + + .main-column-left { + flex-grow: 1; + min-width: 0; + display: flex; + flex-direction: column; } - button { - background-color: #f9f9f9; + + .main-column-right { + flex-shrink: 0; + width: 380px; + height: 100%; + overflow-y: auto; /* only right column scrolls */ + scrollbar-width: thin; + scrollbar-color: #ccc #f4f4f9; + } + + .main-column-right .gallery { + margin-bottom: 24px; } } + +/* ===== Canvas area ===== */ +/* Canvas container and placeholder */ +.canvas-wrapper { + flex-grow: 1; + flex-shrink: 0; + width: 100%; + background-color: #eee; + border-radius: 8px; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + min-height: 250px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +.canvas-wrapper.has-image { + background-color: transparent; + min-height: 0; + align-items: flex-start; + justify-content: flex-start; +} + +.canvas-placeholder { + color: #888; + font-size: 0.9rem; +} + +/* Canvas controls */ +.canvas-controls { + flex-shrink: 0; + display: flex; + justify-content: space-between; + margin: 12px 4px; + flex-wrap: wrap; + gap: 12px; +} + +/* ===== Buttons ===== */ +.btn { + border: none; + color: white; + padding: 8px 12px; + border-radius: 6px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + font-size: 0.9rem; + display: inline-flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + min-width: 36px; + min-height: 36px; +} + +.btn-save { + background-color: #007aff; +} + +.btn:hover { + background-color: #0056b3; +} + +.btn-del-selected { + background-color: #e53935; +} + +.btn-del-selected:hover { + background-color: #b62323; +} + +.button-text { + margin-top: -3px; +} + +.btn .material-symbols-rounded { + font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24; + font-size: 1.3rem; +} + +/* ===== Gallery ===== */ +.gallery { + flex-shrink: 0; + background-color: #fff; + border-radius: 8px; + padding: 16px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + display: flex; + flex-direction: column; + margin-bottom: 16px; + width: 100%; + box-sizing: border-box; + overflow: hidden; +} + +.gallery-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; + gap: 16px; +} +.gallery-header-info { + flex-grow: 1; + display: flex; + align-items: baseline; + gap: 8px; + min-width: 0; +} +.gallery-input { + flex-shrink: 0; + position: sticky; + right: 0px; + background-image: linear-gradient(to right, transparent 0%, #ffffff55 6px, #fff 12px); + z-index: 100; + padding-left: 12px; +} + +.gallery-input .btn { + width: 100px; + height: 100px; + min-width: 100px; + min-height: 100px; + border-radius: 6px; + background-color: transparent; + border: 2px dashed #007aff; + color: #007aff; + transition: all 0.2s; + box-shadow: none; + flex-direction: column; + padding: 8px; + gap: 2px; + font-weight: 500; + cursor: pointer; + font-size: 0.9rem; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.gallery-input .btn:hover { + background-color: rgba(0, 122, 255, 0.05); + border-color: #0056b3; + color: #0056b3; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); +} + +.gallery-input .btn .material-symbols-rounded { + font-size: 32px; +} + +.gallery h3 { + font-size: 1rem; + font-weight: 600; + margin: 0; + flex-shrink: 0; +} + +.gallery p { + font-size: 0.8rem; + color: #666; + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.gallery-container { + width: 100%; + padding: 0; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + position: relative; +} + +.gallery-container::-webkit-scrollbar { + height: 6px; +} + +.gallery-container::-webkit-scrollbar-track { + background: transparent; +} + +.gallery-container::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 3px; +} + +.gallery ul { + list-style: none; + display: flex; + margin-left: -12px; + padding-bottom: 8px; + position: relative; +} + +.gallery li { + flex-shrink: 0; + position: relative; + border-radius: 6px; + overflow: hidden; + margin-left: 12px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.thumbnail { + width: 100px; + height: 100px; + object-fit: cover; + cursor: pointer; + display: block; + background-color: #eee; +} + +.thumbnail:active { + filter: brightness(0.8); +} + +.delete-btn { + position: absolute; + top: 4px; + right: 4px; + width: 24px; + height: 24px; + border-radius: 50%; + background-color: rgba(0, 0, 0, 0.6); + color: white; + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + transition: all 0.2s; +} + +.delete-btn .material-symbols-rounded { + font-size: 16px; +} + +.delete-btn:hover { + background-color: #000; +} + +/* ===== File input (hidden) ===== */ +/* Hide default file input and style label as button */ +.file-input { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; +}