commit 40a48a69c060164e1c0d784f7a9268e2d13e0447
parent fa47da52e3982c32e899af7434cedc7ea71ebc48
Author: Sunny <kajimalu10@gmail.com>
Date: Sun, 9 Nov 2025 16:03:17 +0900
button style
Diffstat:
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;
+}